首页 > Python > 了解 Python super() 和 __init__() 使用方法

了解 Python super() 和 __init__() 使用方法

上一篇 下一篇

网友问题:
为什么使用?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()

分割线

网友回答:

我试图理解super()

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

在 Python 3 中,我们可以这样称呼它:

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

在 Python 2 中,我们需要使用定义类的名称和 进行这样的调用,但从现在开始我们将避免这种情况,因为它是多余的、更慢的(由于名称查找)和更冗长(所以如果你还没有更新你的 Python!superself

        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__()

此代码的主要区别在于,在 中获取了 with 中的间接层,该层使用定义它的类来确定要在 MRO 中查找的下一个类。ChildB__init__super__init__

我在规范问题“如何在Python中使用”super“?的答案中说明了这种差异,它演示了依赖注入协作多重继承

如果蟒蛇没有super

以下是实际上非常等同的代码(它是如何在 C 中实现的,减去一些检查和回退行为,并转换为 Python):super

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

super如何在Python 3中做到这一点,而不会明确告诉它从方法中调用它的哪个类和实例?

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

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

对其他答案的批评:

super() 可以避免显式引用基类,这可能很好。.但主要优势来自多重继承,可以发生各种有趣的事情。如果您还没有,请参阅超级上的标准文档。

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

我将在这里解释。

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__()

Now remember, uses super, does not:ChildBChildA

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__()

并且不调用 UserDependency 方法:UserA

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

但实际上调用用户依赖关系,因为调用:UserBChildBsuper

>>> 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个反对票,回答者坚持建议它,直到一个好心的编辑解决了他的问题。

说明:使用 中作为类名的替代品将导致递归。 让我们在 MRO 中查找子类的下一个父类(请参阅本答案的第一部分)。如果您告诉我们在子实例的方法中,它将查找行中的下一个方法(可能是此方法),从而导致递归,可能会导致逻辑故障(在回答者的示例中,确实如此)或超过递归深度。self.__class__super()supersuperRuntimeError

>>> 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()
分割线

网友回答:

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

请注意,语法在Python 3.0中发生了变化:你可以说哪个IMO更好。标准文档还参考了使用指南,该指南具有很强的解释性。super().__init__()super(ChildB, self).__init__()super()
分割线

网友回答:

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

super().__init__()

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

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

但是,这会中断对从类继承的任何类的调用,其中可能会返回子类。例如:superself.__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,以便我可以重新实现其他一些方法。SquareRectangleSquareRectangle

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

模板简介:该模板名称为【了解 Python super() 和 __init__() 使用方法】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Python】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 389次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 02-09
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
自适应 微信文章 单页式简历模板 html5 微信图片 微信素材 微信公众平台 企业网站 微信模板 响应式
您可能会喜欢的其他模板