【问题标题】:Is there a way to inherit a method that points to a class' own private variable?有没有办法继承指向类自己的私有变量的方法?
【发布时间】:2019-09-10 18:13:15
【问题描述】:

我试图让一个子类从某个父类继承一个方法,该方法指向父类自己的私有变量。我希望孩子的继承方法使用孩子自己的私有变量。在不重新定义子类中的整个方法的情况下,这可能吗?

如果使用公共或受保护(单下划线)变量,代码可以正常工作。但是我们的讲师坚持使用私有(双下划线)变量。我们只接触了几天的面向对象编程,所以我真的不知道我在寻找什么术语(对不起!)。

我怀疑我应该只使用受保护的,但我想尽可能地遵守说明。

class ClassA:
    def __init__(self): self.__var = 10

    def method1(self):
        self.__var += 1     #method1 is intended to work on ClassA's __var
        return self.__var   #The method will also return the processed value of ClassA's __var

class ClassB(ClassA):
    def __init__(self): self.__var = 20

    #ClassA's method1 is inherited by ClassB. 
    #But I want this method1 to use ClassB's __var


ObjectA = ClassA()
ObjectB = ClassB()

print(ObjectA.method1()) #Will output 11
print(ObjectB.method1()) #Will raise an AttributeError, still looks for __var of ClassA

以上只是我打算实现的目标的演示。

当使用公共或受保护变量时,我将得到1121 的输出。
然而,当使用私有变量时,我会收到一个11 和一个错误。

我希望ClassBmethod1 使用ClassB__var (_ClassB__var)。

我可以让ClassB 拥有一个名为_ClassA__var 的变量并将其设置为20,它确实可以工作,但IMO 有点混乱。


编辑:我想我找到了答案。

class ClassA:
    def __init__(self): self.__var = 10

    def method1(self): return self.get_var() + 1

    def get_var(self): return self.__var
    def set_var(self, val): self.__var = val


class ClassB(ClassA):
    def __init__(self): self.__var = 20

    def get_var(self): return self.__var
    def set_var(self, val): self.__var = val


ObjectA = ClassA()
ObjectB = ClassB()

print(ObjectA.method1()) 
print(ObjectB.method1())

我没有直接引用类属性,而是让方法指向一个 getter。这个想法来自这篇文章下的评论。在此编辑中,它不包含在答案中,因此为了清楚起见,我将其放在这里。

谢谢,@艾伦!

【问题讨论】:

  • __-prefixed 名称的目的是防止出现您想要的阴影类型。
  • 如果您的讲师坚持使用双下划线属性,他们可能希望您对这些属性使用 getter 和 setter。 (这不是你在真正的 Python 编程中应该做的事情,但为了这门课程,这可能是你必须做的事情。)
  • @user2357112 感谢您的评论。是的,我们的讲师希望我们包括 getter 和 setter。但是,我想使用父类中的方法,而不是它的属性。我希望这个继承的方法使用孩子自己的属性。
  • 如果您的讲师需要 getter 和 setter,您仍然需要复制它们以使它们同时适用于 ClassAClassB。然后method1 将成为一个可以在两个类中工作的单一实现,因为它会调用这些getter 和setter。
  • @Allan 哦!我想我喜欢这个主意,它很到位。我从来没有想过像那样使用 getter 和 setter。如果您可以将其添加到下面的答案中,我可以将其标记为已接受。 :)

标签: python


【解决方案1】:

不可能让ClassB 使用ClassA__var 而不侵入其命名空间。如您所见,当您使用__var 时,Python 解释器实际上将变量重命名为_classname__variable 之类的名称。这包括method1 中的引用,所以现在method1 将始终引用ClassA 中的__var。如果你真的想要一些疯狂的解决方案,这可能是可能的,但我认为你的导师不会喜欢它,因为你会非常违反封装。

在 Java 等其他语言中,您实际上具有三个访问级别:私有、受保护和公共。 Private 意味着只有同一类的对象才能看到和操作它。 受保护的 是您所追求的,而 Python 在语言中没有。最接近的方法是使用_var,这是 Python 约定,而不是规则。事实上,对于大多数现实世界的生产 Python 代码,一个下划线就足够了,因为它向其他程序员表明“不要乱用这个属性”。至于 public 属性,只需命名变量,前面不带下划线即可。

现在,Python 中确实没有私有属性/变量。有关self._varself.__var 之间差异的更多信息,请参阅此线程:What is the meaning of a single and a double underscore before an object name?


如果你想用ClassA__varClassB对象中调用method1,你需要首先在ClassA的构造函数中初始化ClassA的构造函数(假设Python 3 这里):

class ClassB(ClassA):
    def __init__(self):
        super().__init__()
        self.__var = 20

当您通过super().__init__() 调用该构造函数时,您实际上是在内存中初始化ClassA 的属性。如果你不这样做,它不会初始化任何东西,因此你会得到AttributeError


这是使其工作的一种肮脏方式。请不要在你的作业中这样做。事实上,除非您有非常充分的理由这样做,否则不要在任何代码中这样做:

class ClassA:
    def __init__(self):
        self.__var = 10

    def method1(self):
        varname = "_{}__var".format(self.__class__.__name__)
        v = getattr(self, varname)
        setattr(self, varname, v + 1)
        return getattr(self, varname)

class ClassB(ClassA):
    def __init__(self):
        super().__init__()
        self.__var = 20

【讨论】:

  • 这消除了名称错误(通过确保存在_classA__var),但不会改变ClassA.method1 仍将在该属性上操作的事实,而不是_classB__var(这就是OP想要)。
  • @Allan 谢谢你的回答!但是,正如@chepner 指出的那样,我想用ClassB 自己的__varClassB 对象中调用method1。试用您的代码,我得到了1111 的输出,但我期望1112。干杯。
  • 您的意思是1121?不幸的是,这是不可能的,除非您像我在答案的最后一部分中所做的那样做一些奇怪的事情。你试过最后一个吗?它给出了1121
  • @Allan 哎呀。我实际上在等待您的编辑,因为我认为您在看到第一条评论后已经编辑了您的答案。我想我只看到了第一部分,对此感到抱歉。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-14
  • 1970-01-01
  • 2011-02-16
  • 2010-12-27
  • 2013-01-01
  • 1970-01-01
相关资源
最近更新 更多