【问题标题】:Why does the child class does not inherit the method from parent class in python in this example?为什么在这个例子中子类没有从python中继承父类的方法?
【发布时间】:2013-10-03 06:20:04
【问题描述】:

我有以下代码。

class Foo(object):
     def __init__(self):
         self.__baz = 40
     def foo(self):
         print self.__baz

class Bar(Foo):
     def __init__(self):
         #super(Bar, self).__init__()
         self.__baz = 21
     def bar(self):
         print self.__baz

x = Bar()
x.foo()
x.bar()

我收到此错误:

Traceback (most recent call last):
  File "classes.py", line 15, in <module>
    x.foo()
  File "classes.py", line 5, in foo
    print self.__baz
AttributeError: 'Bar' object has no attribute '_Foo__baz'

为什么foo 方法没有在Bar 中继承。

编辑:如果你调用 super 被注释掉,它工作正常。

【问题讨论】:

  • 不确定,但不是因为 __variablename 是一个特殊变量吗? stackoverflow.com/a/1301369/2537322
  • 更奇怪的是,如果你打电话给super(),为什么它工作
  • 不是答案,但会添加更多详细信息 - 如果您将其从 __baz 更改为 baz,则两个调用都将打印 21。如果您调用 super() 并保留为 __baz,则它们会打印 @987654332分别是@和21

标签: python oop inheritance


【解决方案1】:

从python 3.6开始,我们现在可以使用__init_subclass__函数,该函数在Child的__init__之前自动调用。

class Foo(object):
     def __init_subclass__(self):
         self.__baz = 40
     def foo(self):
         print(self.__baz)

class Bar(Foo):
     def __init__(self):
         self.__baz = 21
     def bar(self):
         print(self.__baz)

x = Bar()
x.foo()
x.bar()

输出

40
21

【讨论】:

    【解决方案2】:

    双下划线属性根据 current/containing 命名空间将它们的名称修改。在函数foo 中,当前命名空间是Foo,因此当python 查找self.__baz 时,由于名称修改方案,它实际上会查找self._Foo__baz。由于在Foo 中没有任何地方您实际上设置了__baz 属性,因此该类没有_Foo__baz 属性(它具有_Bar__baz 属性,因为您在self.__baz 中的方法中设置了self.__baz)。

    当然,您可能已经注意到,如果您在Baz.__init__ 中调用Foo.__init__(self)(直接或通过super),您会看到问题消失了,因为Foo.__init__ 设置了__baz(即_Foo__baz)。

    【讨论】:

    • 所以 foo 方法没有被正确继承?我对这里发生的事情有点困惑......
    • @user1988876 -- foo 方法继承。但是当你调用它时,它试图访问的属性是missing。当 python 在Bar 类的定义中看到self.__baz 时,它会将其更改为self._Bar__baz。当它在Foo 类的定义中看到self.__baz 时,会将其更改为self._Foo__baz。这里的整个想法是使Foo 内部的self.__baz 可以与Bar 内部的self.__baz 不同。
    • 所以实际上当他调用super(Bar, self).__init__()子类Bar的对象有两个属性一个是_Bar__baz(因为他在子类init方法中做了self.__baz = 21)和另一个是来自父类的_Foo__baz ? 而在错误情况下(如果他不调用super(Bar, self).__init__())Bar 的对象只有一个属性_Bar__baz ?
    • @GrijeshChauhan -- 没错。
    • 表示Foo.__init__Foo命名空间中。 Bar.__init__ 位于 Bar 命名空间中。当 python 创建一个类时,它会创建一个新的命名空间(有效地使用类的名称)。该类中的所有函数和变量都属于该命名空间。因此,当您在 Foo.__init__ 中时,“当前命名空间”为 Foo
    【解决方案3】:

    当你像在 python 中那样使用双下划线命名变量时,成员名称将被混淆。声明 __baz 会给你一个成员 _Bar__baz

    class Bar(Foo):
     def __init__(self):
         #super(Bar, self).__init__()
         self.__baz = 21
     def bar(self):
         print self._Bar__baz
    
    x = Bar()
    x.bar()
    >>> 21
    

    【讨论】:

    • 你改变了函数bar的定义。我想知道为什么在进行超级调用时它会起作用,就像我在编辑中所做的那样
    • + 因为您添加了一个对我有帮助的技巧,但是明确使用_Bar__baz 不是不好的做法
    【解决方案4】:

    通过在__baz 上使用最初的双下划线,您请求“名称修改”来创建一个“私有”变量。它记录在这里:

    http://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references

    如果您将名称从 __baz 更改为 baz,您的代码将如图所示运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-16
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 2020-02-09
      • 1970-01-01
      相关资源
      最近更新 更多