【问题标题】:Python extending with - using super() Python 3 vs Python 2Python 扩展 - 使用 super() Python 3 与 Python 2
【发布时间】:2012-05-16 00:17:48
【问题描述】:

本来想问this question,后来发现之前已经想到了……

谷歌搜索我发现了extending configparser 的这个例子。以下适用于 Python 3:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init__(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

但不是 Python 2:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

然后我阅读了一些关于 Python 新类与旧类样式的内容(例如 here. 现在我想知道,我能做到:

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

但是,我不应该调用 init 吗?这是在 Python 2 中的等价物吗:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)

【问题讨论】:

  • 在您的示例中,您不需要在子类中定义__init__(),如果它所做的只是调用超类'__init__()(在 Python 2 或 3 中)——而只需让超人被继承。
  • 有更正链接的有用参考:amyboyle.ninja/Python-Inheritance

标签: python inheritance configparser


【解决方案1】:

在单一继承的情况下(当您只继承一个类时),您的新类会继承基类的方法。这包括__init__。所以如果你没有在你的类中定义它,你会从基础中得到它。

如果您引入多重继承(一次子类化多个类),事情就会变得复杂。这是因为如果多个基类有__init__,您的类将只继承第一个。

在这种情况下,如果可以的话,你真的应该使用super,我会解释原因。但并非总是可以。问题是您的所有基类也必须使用它(以及它们的基类——整个树)。

如果是这种情况,那么它也可以正常工作(在 Python 3 中,但您可以将其重新编写到 Python 2 中——它也有 super):

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

请注意两个基类如何使用super,即使它们没有自己的基类。

super 所做的是:它从 MRO 中的下一个类调用方法(方法解析顺序)。 C 的 MRO 是:(C, A, B, object)。你可以打印C.__mro__来查看。

所以,C 继承自 A__init__A.__init__ 中的 super 调用 B.__init__B 遵循 MRO 中的 A)。

因此,如果在 C 中什么都不做,你最终会调用两者,这就是你想要的。

现在,如果您不使用 super,您最终会继承 A.__init__(和以前一样),但这次没有什么可以为您调用 B.__init__

class A:
    def __init__(self):
        print('A')

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

要解决这个问题,您必须定义C.__init__

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

问题在于,在更复杂的 MI 树中,某些类的 __init__ 方法最终可能会被多次调用,而 super/MRO 保证它们只被调用一次。

【讨论】:

  • Notice how both base classes use super even though they don't have their own base classes. 他们有。在 py3k 中,每个类都是对象的子类。
  • 这是我一直在寻找的答案,但不知道如何问。 MRO 描述很好。
【解决方案2】:
  • super()(不带参数)是在 Python 3 中引入的(连同__class__):

    super() -> same as super(__class__, self)
    

    所以这将是 Python 2 中新样式类的等价物:

    super(CurrentClass, self)
    
  • 对于旧式类,您可以随时使用:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
    

【讨论】:

  • -1。这个答案没有为我澄清任何事情。在 Python 2 中,super(__class__) 给出了NameError: global name '__class__' is not defined,而super(self.__class__) 也是错误的。您必须提供一个实例作为第二个参数,这表明您需要执行super(self.__class__, self),但这是错误。如果Class2 继承自Class1 并且Class1 调用super(self.__class__, self).__init__(),则Class1__init__ 将在实例化Class2 的实例时调用自身
  • 为了澄清一点,我在 Python 2 中尝试调用 super(self.__class__) 时得到了 TypeError: super() takes at least 1 argument (0 given)。(这没有多大意义,但它证明了其中缺少多少信息回答。)
  • @jpmc26: 在 python2 中,您会收到此错误,因为您尝试在未绑定的超级对象上不带参数调用 __init__()(通过仅使用一个参数调用 super(self.__class__) 获得),您需要一个绑定超级对象然后它应该可以工作:super(CurrentClass, self).__init__()。不要使用self.__class__,因为在调用父级时它总是引用 same 类,因此如果父级也这样做,则会创建一个无限循环。
  • __class__(成员)也存在于Python2中。
  • @CristiFati 这不是关于__class__ 成员,而是关于implicitly created lexical __class__ closure,它总是指当前正在定义的类,它在python2 中不存在。
【解决方案3】:

简而言之,它们是等价的。 让我们看一个历史视图:

(1) 一开始,函数是这样的。

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2) 使代码更抽象(并且更便携)。一种常见的获取超类的方法是这样发明的:

    super(<class>, <instance>)

而init函数可以是:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

但是,需要明确传递类和实例会稍微违反 DRY(不要重复自己)规则。

(3) 在 V3 中。它更聪明,

    super()

在大多数情况下就足够了。可以参考http://www.python.org/dev/peps/pep-3135/

【讨论】:

    【解决方案4】:

    只是为 Python 3 提供一个简单而完整的示例,大多数人现在似乎都在使用它。

    class MySuper(object):
        def __init__(self,a):
            self.a = a
    
    class MySub(MySuper):
        def __init__(self,a,b):
            self.b = b
            super().__init__(a)
    
    my_sub = MySub(42,'chickenman')
    print(my_sub.a)
    print(my_sub.b)
    

    给予

    42
    chickenman
    

    【讨论】:

      【解决方案5】:

      另一个 python3 实现涉及使用带有 super() 的抽象类。你应该记住

      super().__init__(name, 10)
      

      效果和

      一样
      Person.__init__(self, name, 10)
      

      记住在 super() 中有一个隐藏的“self”,所以同一个对象传递给超类的 init 方法,并且属性被添加到调用它的对象中。 因此super()被翻译成Person,然后如果你包含隐藏的自我,你会得到上面的代码片段。

      from abc import ABCMeta, abstractmethod
      class Person(metaclass=ABCMeta):
          name = ""
          age = 0
      
          def __init__(self, personName, personAge):
              self.name = personName
              self.age = personAge
      
          @abstractmethod
          def showName(self):
              pass
      
          @abstractmethod
          def showAge(self):
              pass
      
      
      class Man(Person):
      
          def __init__(self, name, height):
              self.height = height
              # Person.__init__(self, name, 10)
              super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
              # basically used to call the superclass init . This is used incase you want to call subclass init
              # and then also call superclass's init.
              # Since there's a hidden self in the super's parameters, when it's is called,
              # the superclasses attributes are a part of the same object that was sent out in the super() method
      
          def showIdentity(self):
              return self.name, self.age, self.height
      
          def showName(self):
              pass
      
          def showAge(self):
              pass
      
      
      a = Man("piyush", "179")
      print(a.showIdentity())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-04
        • 1970-01-01
        • 2018-09-04
        • 2011-03-08
        • 1970-01-01
        相关资源
        最近更新 更多