理解 Python super() 和 __init__() 方法

发布于 2024-07-13 23:25:13 字数 411 浏览 8 评论 0原文

为什么使用super()

使用 Base.__init__super().__init__ 之间有区别吗?

class Base(object):
    def __init__(self):
        print "Base created"
        
class ChildA(Base):
    def __init__(self):
        Base.__init__(self)
        
class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()
        
ChildA() 
ChildB()

Why is super() used?

Is there a difference between using Base.__init__ and super().__init__?

class Base(object):
    def __init__(self):
        print "Base created"
        
class ChildA(Base):
    def __init__(self):
        Base.__init__(self)
        
class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()
        
ChildA() 
ChildB()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

我不是你的备胎 2024-07-20 23:25:13

super() 可以让您避免显式引用基类,这很好。 但主要优点是多重继承,其中有各种有趣的东西可以发生。 如果您还没有看过有关 super 的标准文档,请参阅。

请注意,Python 3.0 中的语法已更改:您可以只说 super().__init__() 而不是 super(ChildB, self).__init__() IMO 更好一些。 标准文档还参考了 使用 super() 的指南 这是非常有解释性的。

super() lets you avoid referring to the base class explicitly, which can be nice. But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.

Note that the syntax changed in Python 3.0: you can just say super().__init__() instead of super(ChildB, self).__init__() which IMO is quite a bit nicer. The standard docs also refer to a guide to using super() which is quite explanatory.

寄与心 2024-07-20 23:25:13

我试图理解super()

我们使用 super 的原因是,可能使用协作多重继承的子类将调用正确的下一个父类函数方法解析顺序 (MRO)。

在Python 3中,我们可以这样调用它:

class ChildB(Base):
    def __init__(self):
        super().__init__()

在Python 2中,我们需要使用定义类的名称和self来调用super,但我们会避免从现在开始,因为它是多余的,更慢(由于名称查找),而且更冗长(所以如果你还没有更新你的Python,请更新它!):如果

        super(ChildB, self).__init__()

没有 super,你使用多重继承的能力就会受到限制,因为你很难- 转接下一位家长的电话:

        Base.__init__(self) # Avoid this.

我在下面进一步解释。

“这段代码实际上有什么区别?:”

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super().__init__()

这段代码的主要区别在于,在 ChildB 中,您在 __init__ 中使用 super 获得了一个间接层,它使用定义它的类来确定要在 MRO 中查找的下一个类的 __init__

我在规范问题“How to use 'super' in Python?”的答案中说明了这种差异,该问题演示了依赖注入协作多重继承

如果 Python 没有 super

下面的代码实际上与 super 非常接近(它是如何在 C 中实现的,减去一些检查和后备行为,然后翻译成 Python)

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        check_next = mro.index(ChildB) + 1 # next after *this* class.
        while check_next < len(mro):
            next_class = mro[check_next]
            if '__init__' in next_class.__dict__:
                next_class.__init__(self)
                break
            check_next += 1

:有点像原生 Python:

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
            if hasattr(next_class, '__init__'):
                next_class.__init__(self)
                break

如果我们没有 super 对象,我们必须在任何地方编写此手动代码(或重新创建它!)以确保我们调用正确的 next 方法方法解析顺序!

super 在 Python 3 中如何做到这一点,而无需明确告知调用它的方法是哪个类和实例?

它获取调用堆栈帧,并找到该类(隐式存储为本地自由变量,__class__,使调用函数成为该类的闭包)和该函数的第一个参数,该参数应该是通知它使用哪种方法解析顺序 (MRO) 的实例或类。

由于它需要 MRO 的第一个参数,因此 super 与静态方法一起使用是不可能的无法访问调用它们的类的 MRO

对其他答案的批评:

super() 让您可以避免显式引用基类,这很好。 。 但主要优点是多重继承,可以发生各种有趣的事情。 如果您还没有看过 super 的标准文档,请参阅。

它相当简单,并没有告诉我们太多信息,但 super 的要点并不是避免编写父类。 重点是确保调用方法解析顺序 (MRO) 中的下一个方法。 这在多重继承中变得很重要。

我在这里解释一下。

class Base(object):
    def __init__(self):
        print("Base init'ed")

class ChildA(Base):
    def __init__(self):
        print("ChildA init'ed")
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        print("ChildB init'ed")
        super().__init__()

让我们创建一个要在 Child 之后调用的依赖项:

class UserDependency(Base):
    def __init__(self):
        print("UserDependency init'ed")
        super().__init__()

现在记住,ChildB 使用 super,ChildA 不使用:

class UserA(ChildA, UserDependency):
    def __init__(self):
        print("UserA init'ed")
        super().__init__()

class UserB(ChildB, UserDependency):
    def __init__(self):
        print("UserB init'ed")
        super().__init__()

UserA 则使用 super不调用 UserDependency 方法:

>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>

UserB 实际上调用 UserDependency 因为 ChildB 调用 super

>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>

对另一个答案的批评

在任何情况下都不应该这样做下面是另一个答案所暗示的,因为当你对 ChildB 进行子类化时,你肯定会遇到错误:

super(self.__class__, self).__init__()  # DON'T DO THIS! EVER.

(这个答案并不聪明或特别有趣,但尽管评论中存在直接批评和超过 17 票反对,但回答者仍然坚持直到一位好心的编辑解决了他的问题。) 说明

:使用 self.__class__ 作为在 super() 中按名称显式传递类的替代品会导致递归。 super 让我们在 MRO 中查找子类的下一个父类(请参阅本答案的第一部分)。 如果我们告诉 super 我们在子方法中,它就会查找行中的下一个方法(可能是我们调用它的同一个方法),导致递归,导致逻辑失败(如在回答者的示例中)或超过最大递归深度时的 RuntimeError

class Polygon(object):
    def __init__(self, id):
        self.id = id

class Rectangle(Polygon):
    def __init__(self, id, width, height):
        super(self.__class__, self).__init__(id)
        self.shape = (width, height)

class Square(Rectangle):
    pass

>>> Square('a', 10, 10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'

幸运的是,Python 3 的新的不带参数的 super() 调用方法允许我们回避这个问题。

I'm trying to understand super()

The reason we use super is so that child classes that may be using cooperative multiple inheritance will call the correct next parent class function in the Method Resolution Order (MRO).

In Python 3, we can call it like this:

class ChildB(Base):
    def __init__(self):
        super().__init__()

In Python 2, we were required to call super like this with the defining class's name and self, but we'll avoid this from now on because it's redundant, slower (due to the name lookups), and more verbose (so update your Python if you haven't already!):

        super(ChildB, self).__init__()

Without super, you are limited in your ability to use multiple inheritance because you hard-wire the next parent's call:

        Base.__init__(self) # Avoid this.

I further explain below.

"What difference is there actually in this code?:"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super().__init__()

The primary difference in this code is that in ChildB you get a layer of indirection in the __init__ with super, which uses the class in which it is defined to determine the next class's __init__ to look up in the MRO.

I illustrate this difference in an answer at the canonical question, How to use 'super' in Python?, which demonstrates dependency injection and cooperative multiple inheritance.

If Python didn't have super

Here's code that's actually closely equivalent to super (how it's implemented in C, minus some checking and fallback behavior, and translated to Python):

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        check_next = mro.index(ChildB) + 1 # next after *this* class.
        while check_next < len(mro):
            next_class = mro[check_next]
            if '__init__' in next_class.__dict__:
                next_class.__init__(self)
                break
            check_next += 1

Written a little more like native Python:

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
            if hasattr(next_class, '__init__'):
                next_class.__init__(self)
                break

If we didn't have the super object, we'd have to write this manual code everywhere (or recreate it!) to ensure that we call the proper next method in the Method Resolution Order!

How does super do this in Python 3 without being told explicitly which class and instance from the method it was called from?

It gets the calling stack frame, and finds the class (implicitly stored as a local free variable, __class__, making the calling function a closure over the class) and the first argument to that function, which should be the instance or class that informs it which Method Resolution Order (MRO) to use.

Since it requires that first argument for the MRO, using super with static methods is impossible as they do not have access to the MRO of the class from which they are called.

Criticisms of other answers:

super() lets you avoid referring to the base class explicitly, which can be nice. . But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.

It's rather hand-wavey and doesn't tell us much, but the point of super is not to avoid writing the parent class. The point is to ensure that the next method in line in the method resolution order (MRO) is called. This becomes important in multiple inheritance.

I'll explain here.

class Base(object):
    def __init__(self):
        print("Base init'ed")

class ChildA(Base):
    def __init__(self):
        print("ChildA init'ed")
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        print("ChildB init'ed")
        super().__init__()

And let's create a dependency that we want to be called after the Child:

class UserDependency(Base):
    def __init__(self):
        print("UserDependency init'ed")
        super().__init__()

Now remember, ChildB uses super, ChildA does not:

class UserA(ChildA, UserDependency):
    def __init__(self):
        print("UserA init'ed")
        super().__init__()

class UserB(ChildB, UserDependency):
    def __init__(self):
        print("UserB init'ed")
        super().__init__()

And UserA does not call the UserDependency method:

>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>

But UserB does in-fact call UserDependency because ChildB invokes super:

>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>

Criticism for another answer

In no circumstance should you do the following, which another answer suggests, as you'll definitely get errors when you subclass ChildB:

super(self.__class__, self).__init__()  # DON'T DO THIS! EVER.

(That answer is not clever or particularly interesting, but in spite of direct criticism in the comments and over 17 downvotes, the answerer persisted in suggesting it until a kind editor fixed his problem.)

Explanation: Using self.__class__ as a substitute for explicitly passing the class by name in super() will lead to recursion. super lets us look up the next parent in the MRO (see the first section of this answer) for child classes. If we tell super we're in the child's method, it will then lookup the next method in line (probably this same one we are calling it from) resulting in recursion, causing either a logical failure (as in the answerer's example) or a RuntimeError when the maximum recursion depth is exceeded.

class Polygon(object):
    def __init__(self, id):
        self.id = id

class Rectangle(Polygon):
    def __init__(self, id, width, height):
        super(self.__class__, self).__init__(id)
        self.shape = (width, height)

class Square(Rectangle):
    pass

>>> Square('a', 10, 10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'

Python 3's new super() calling method with no arguments fortunately allows us to sidestep this issue.

淑女气质 2024-07-20 23:25:13

值得注意的是,在 Python 3.0+ 中,您可以使用

super().__init__()

来进行调用,这很简洁,并且不需要您显式引用父类或类名,这很方便。 我只想补充一点,对于 Python 2.7 或更低版本,有些人通过编写 self.__class__ 而不是类名来实现名称不敏感的行为,即,但是

super(self.__class__, self).__init__()  # DON'T DO THIS!

,这会中断对 super< 的调用/code> 对于从您的类继承的任何类,其中 self.__class__ 可以返回子类。 例如:

class Polygon(object):
    def __init__(self, id):
        self.id = id

class Rectangle(Polygon):
    def __init__(self, id, width, height):
        super(self.__class__, self).__init__(id)
        self.shape = (width, height)

class Square(Rectangle):
    pass

这里我有一个类Square,它是Rectangle的子类。 假设我不想为 Square 编写单独的构造函数,因为 Rectangle 的构造函数已经足够好了,但无论出于何种原因,我想实现 Square,以便可以重新实现其他一些方法。

当我使用 mSquare = Square('a', 10,10) 创建 Square 时,Python 会调用 Rectangle 的构造函数,因为我还没有' t 给 Square 它自己的构造函数。 但是,在 Rectangle 的构造函数中,调用 super(self.__class__,self) 将返回 mSquare 的超类,因此它再次调用 Rectangle 的构造函数。 正如 @S_C 所提到的,这就是无限循环发生的方式。 在这种情况下,当我运行 super(...).__init__() 时,我正在调用 Rectangle 的构造函数,但由于我没有给它任何参数,所以我会得到一个错误。

It's been noted that in Python 3.0+ you can use

super().__init__()

to make your call, which is concise and does not require you to reference the parent OR class names explicitly, which can be handy. I just want to add that for Python 2.7 or under, some people implement a name-insensitive behaviour by writing self.__class__ instead of the class name, i.e.

super(self.__class__, self).__init__()  # DON'T DO THIS!

HOWEVER, this breaks calls to super for any classes that inherit from your class, where self.__class__ could return a child class. For example:

class Polygon(object):
    def __init__(self, id):
        self.id = id

class Rectangle(Polygon):
    def __init__(self, id, width, height):
        super(self.__class__, self).__init__(id)
        self.shape = (width, height)

class Square(Rectangle):
    pass

Here I have a class Square, which is a sub-class of Rectangle. Say I don't want to write a separate constructor for Square because the constructor for Rectangle is good enough, but for whatever reason I want to implement a Square so I can reimplement some other method.

When I create a Square using mSquare = Square('a', 10,10), Python calls the constructor for Rectangle because I haven't given Square its own constructor. However, in the constructor for Rectangle, the call super(self.__class__,self) is going to return the superclass of mSquare, so it calls the constructor for Rectangle again. This is how the infinite loop happens, as was mentioned by @S_C. In this case, when I run super(...).__init__() I am calling the constructor for Rectangle but since I give it no arguments, I will get an error.

执着的年纪 2024-07-20 23:25:13

Super 没有副作用

Base = ChildB

Base()

,按预期

Base = ChildA

Base()

进入无限递归。

Super has no side effects

Base = ChildB

Base()

works as expected

Base = ChildA

Base()

gets into infinite recursion.

画▽骨i 2024-07-20 23:25:13

请注意......使用Python 2.7,我相信自从在版本2.2中引入 super() 以来,你只能调用 super() 如果父级之一继承自最终继承 object 的类( 新样式类)。

就我个人而言,对于 python 2.7 代码,我将继续使用 BaseClassName.__init__(self, args) 直到我真正获得使用 super() 的优势。

Just a heads up... with Python 2.7, and I believe ever since super() was introduced in version 2.2, you can only call super() if one of the parents inherit from a class that eventually inherits object (new-style classes).

Personally, as for python 2.7 code, I'm going to continue using BaseClassName.__init__(self, args) until I actually get the advantage of using super().

A君 2024-07-20 23:25:13

确实没有。 super() 查看 MRO 中的下一个类(方法解析顺序,通过 cls.__mro__ 访问)来调用方法。 只需调用基 __init__ 即可调用基 __init__。 碰巧的是,MRO 只有一件物品——底座。 因此,您确实在做完全相同的事情,但使用 super() 的方式更好(特别是如果您稍后进入多重继承)。

There isn't, really. super() looks at the next class in the MRO (method resolution order, accessed with cls.__mro__) to call the methods. Just calling the base __init__ calls the base __init__. As it happens, the MRO has exactly one item-- the base. So you're really doing the exact same thing, but in a nicer way with super() (particularly if you get into multiple inheritance later).

人│生佛魔见 2024-07-20 23:25:13

主要区别在于 ChildA.__init__ 将无条件调用 Base.__init__ChildB.__init__ 将调用 __init__ 无论哪个类恰好是 self 祖先行中的 ChildB 祖先
(这可能与您的期望不同)。

如果您添加使用多重继承的 ClassC

class Mixin(Base):
  def __init__(self):
    print "Mixin stuff"
    super(Mixin, self).__init__()

class ChildC(ChildB, Mixin):  # Mixin is now between ChildB and Base
  pass

ChildC()
help(ChildC) # shows that the Method Resolution Order is ChildC->ChildB->Mixin->Base

Base 不再是 ChildB 的父级>ChildC 实例。 现在,如果 selfChildC 实例,则 super(ChildB, self) 将指向 Mixin

您已在 ChildBBase 之间插入 Mixin。 您可以通过 super() 来利用它。

因此,如果您设计的类可以在协作多重继承场景中使用,那么您可以使用 super,因为您在运行时并不真正知道谁将成为祖先。

超级被视为超级帖子pycon 2015 随附视频很好地解释了这一点。

The main difference is that ChildA.__init__ will unconditionally call Base.__init__ whereas ChildB.__init__ will call __init__ in whatever class happens to be ChildB ancestor in self's line of ancestors
(which may differ from what you expect).

If you add a ClassC that uses multiple inheritance:

class Mixin(Base):
  def __init__(self):
    print "Mixin stuff"
    super(Mixin, self).__init__()

class ChildC(ChildB, Mixin):  # Mixin is now between ChildB and Base
  pass

ChildC()
help(ChildC) # shows that the Method Resolution Order is ChildC->ChildB->Mixin->Base

then Base is no longer the parent of ChildB for ChildC instances. Now super(ChildB, self) will point to Mixin if self is a ChildC instance.

You have inserted Mixin in between ChildB and Base. And you can take advantage of it with super()

So if you are designed your classes so that they can be used in a Cooperative Multiple Inheritance scenario, you use super because you don't really know who is going to be the ancestor at runtime.

The super considered super post and pycon 2015 accompanying video explain this pretty well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文