什么是';超级';你用Python做什么super().\uuuu init\uuuuuu()和显式超类\uuuuu init\uuuuuu()之间的差异
这两者之间的区别是什么:什么是';超级';你用Python做什么super().\uuuu init\uuuuuu()和显式超类\uuuuu init\uuuuuu()之间的差异,python,oop,inheritance,multiple-inheritance,super,Python,Oop,Inheritance,Multiple Inheritance,Super,这两者之间的区别是什么: class Child(SomeBaseClass): def __init__(self): super(Child, self).__init__() 以及: 我见过super在只有单一继承的类中被大量使用。我可以理解为什么要在多重继承中使用它,但不清楚在这种情况下使用它有什么好处。在单一继承中super()的好处是最小的——大多数情况下,您不必将基类的名称硬编码到使用其父方法的每个方法中 但是,如果没有super(),几乎不可能使用多重继
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
以及:
我见过
super
在只有单一继承的类中被大量使用。我可以理解为什么要在多重继承中使用它,但不清楚在这种情况下使用它有什么好处。在单一继承中super()
的好处是最小的——大多数情况下,您不必将基类的名称硬编码到使用其父方法的每个方法中
但是,如果没有
super()
,几乎不可能使用多重继承。这包括常见的习惯用法,如mixin、接口、抽象类等。这将扩展到以后扩展您的代码。如果后来有人想编写一个扩展了子类和mixin的类,那么他们的代码将无法正常工作。所有这些不都假设基类是一个新样式的类吗
class A:
def __init__(self):
print("A.__init__()")
class B(A):
def __init__(self):
print("B.__init__()")
super(B, self).__init__()
在Python2中不起作用<代码>A类
必须是新型的,即:A类(对象)
有什么区别?
表示调用SomeBaseClass
的\uuuu init\uuuu
。当
super(Child, self).__init__()
表示从实例的方法解析顺序(MRO)中的子类之后的父类调用绑定的\uuuuu init\uuuu
如果该实例是Child的子类,那么在MRO中可能会出现另一个父类
简单解释
当您编写一个类时,您希望其他类能够使用它super()
使其他类更容易使用您正在编写的类
正如Bob Martin所说,良好的体系结构允许您尽可能长时间地推迟决策
super()
可以启用这种体系结构
当另一个类对您编写的类进行子类化时,它也可以从其他类继承。这些类可以有一个\uuuuu init\uuuuu
,该\uuuuuu init\uuuuuu
基于类对方法解析的排序
如果没有super
,您可能会对正在编写的类的父类进行硬编码(如示例所示)。这意味着您不会在MRO中调用下一个\uuuu init\uuuu
,因此无法在其中重用代码
如果您正在编写自己的代码供个人使用,那么您可能不关心这种区别。但是,如果您希望其他人使用您的代码,那么使用super
是允许代码用户具有更大灵活性的一件事
Python 2对3
这在Python 2和Python 3中适用:
super(Child, self).__init__()
这仅适用于Python 3:
super().__init__()
In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)
它通过在堆栈框架中向上移动并获取方法的第一个参数(对于实例方法通常是self
,对于类方法通常是cls
,但是可以是其他名称)并在自由变量中查找类(例如Child
)(在方法中以名称\uuuuu class\uuuu
作为自由闭包变量进行查找)
我更愿意演示使用super
的交叉兼容方式,但如果您只使用Python 3,则可以不带参数地调用它
具有前向兼容性的间接寻址
它给了你什么?对于单一继承,从静态分析的角度来看,问题中的示例实际上是相同的。但是,使用super
给了你一层具有前向兼容性的间接层
前向兼容性对于经验丰富的开发人员来说非常重要。您希望代码在更改时保持最小的更改。当您查看修订历史时,您希望准确地看到更改的内容
您可以从单一继承开始,但是如果您决定添加另一个基类,您只需要更改基类的行-如果您继承的类中的基类发生了更改(例如添加了mixin)在这个类中,您不会做任何更改。尤其是在Python 2中,获得super
的参数和正确的方法参数可能会很困难。如果您知道您在单继承中正确使用了super
,这将降低调试的难度
依赖注入
其他人可以使用您的代码并将父项注入到方法解析中:
class SomeBaseClass(object):
def __init__(self):
print('SomeBaseClass.__init__(self) called')
class UnsuperChild(SomeBaseClass):
def __init__(self):
print('UnsuperChild.__init__(self) called')
SomeBaseClass.__init__(self)
class SuperChild(SomeBaseClass):
def __init__(self):
print('SuperChild.__init__(self) called')
super(SuperChild, self).__init__()
假设您向对象添加了另一个类,并希望在Foo和Bar之间插入一个类(出于测试或其他原因):
使用un super子级无法注入依赖项,因为您正在使用的子级已硬编码要在其自己的子级之后调用的方法:
>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called
但是,使用super
的子类可以正确地注入依赖项:
>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called
发表评论
这到底为什么有用呢
Python通过将复杂的继承树线性化来创建方法解析顺序(MRO)
我们希望按照这个顺序查找方法
对于在父级中定义的方法,如果不使用super
,则必须按该顺序查找下一个方法
从实例的类型获取mro
查找定义该方法的类型
使用该方法查找下一个类型
绑定该方法并使用预期参数调用它
unperspechild
不应该访问InjectMe
。为什么结论不是“总是避免使用super
”?我在这里遗漏了什么
unperhild
无权访问InjectMe
。只有unperhilder
才有权访问InjectMe
,但无法从继承自unperhild
的方法调用该类的方法
两个子类都打算用MRO中接下来出现的相同名称调用一个方法,这可能是它在创建时不知道的另一个类
没有super
硬编码其父方法的方法-因此
>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called
>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called
super(B, b)
# resolves to the scope of B's parent i.e. A
# and applies that scope to b, as if b was an instance of A
super(C, c)
# resolves to the scope of C's parent i.e. B
# and applies that scope to c
super(B, c)
# resolves to the scope of B's parent i.e. A
# and applies that scope to c
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
# if you defined this
class A(object):
def __new__(cls):
pass
# calling this would raise a TypeError due to the missing argument
A.__new__()
# whereas this would be fine
A.__new__(A)
super(A, cls)
super(A, cls).__new__(cls, *a, **kw)
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...
# you create an instance
a = A()
# you call `__init__()` from that instance and it works
a.__init__()
# you can also call `__init__()` with the class and explicitly pass the instance
A.__init__(a)
super(A, self)
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
class A(object):
@classmethod
def alternate_constructor(cls, *a, **kw):
print "A.alternate_constructor called"
return cls(*a, **kw)
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return super(B, cls).alternate_constructor(*a, **kw)
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
super(B, cls_or_subcls)
super(B, cls).alternate_constructor()
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return A.alternate_constructor(cls, *a, **kw)
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
# first we get a reference to the unbound
# `A.alternate_constructor` function
unbound_func = A.alternate_constructor.im_func
# now we call it and pass our own `cls` as its first argument
return unbound_func(cls, *a, **kw)
A
/ \
B C
\ /
D
In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)
In [23]: class A(object): # or with Python 3 can define class A:
...: def __init__(self):
...: print("I'm from A")
...:
...: class B(A):
...: def __init__(self):
...: print("I'm from B")
...: super().__init__()
...:
...: class C(A):
...: def __init__(self):
...: print("I'm from C")
...: super().__init__()
...:
...: class D(B, C):
...: def __init__(self):
...: print("I'm from D")
...: super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A
A
/ ⇖
B ⇒ C
⇖ /
D
In [21]: class A(object): # or class A:
...: def __init__(self):
...: print("I'm from A")
...:
...: class B(A):
...: def __init__(self):
...: super().__init__() # or super(B, self).__init_()
...: print("I'm from B")
...:
...: class C(A):
...: def __init__(self):
...: super().__init__()
...: print("I'm from C")
...:
...: class D(B, C):
...: def __init__(self):
...: super().__init__()
...: print("I'm from D")
...: d = D()
...:
I'm from A
I'm from C
I'm from B
I'm from D
A
/ ⇘
B ⇐ C
⇘ /
D
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
E name=python age=28
C age=28
A
D name=python
B
SuperObject
Class Jen(Cain, Sue):
X
Y
Q
X
P
Y
Q
class Base(object):
def __init__(self):
print("initializing Base")
class ChildA(Base):
def __init__(self):
print("initializing ChildA")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("initializing ChildB")
super().__init__()
class Grandchild(ChildA, ChildB):
def __init__(self):
print("initializing Grandchild")
super().__init__()
Grandchild()
initializing Grandchild
initializing ChildA
initializing Base
initializing Grandchild
initializing ChildA
initializing ChildB
initializing Base
class A:
def m(self):
print('A')
class B(A):
def m(self):
print('B start')
super().m()
print('B end')
class C(A):
def m(self):
print('C start')
super().m()
print('C end')
class D(B, C):
def m(self):
print('D start')
super().m()
print('D end')
>>> a = A()
>>> b = B()
>>> c = C()
>>> d = D()
>>> a.m()
A
>>> type(b).__mro__
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
>>> b.m()
B start
A
B end
>>> type(c).__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> b.m()
C start
A
C end
>>> type(d).__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> d.m()
D start
B start
C start
A
C end
B end
D end