当调用super()解析到父类方法、实例方法或static方法的父版本时,我们希望将我们所在作用域的当前类作为第一个参数传递,以指示我们试图解析到哪个父作用域,并将感兴趣的对象作为第二个参数传递,以指示我们试图将该作用域应用到哪个对象。
考虑一个类层次结构a、B和C,其中每个类都是后面一个类的父类,并且a、B和C分别是每个类的实例。
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
使用super和static方法
例如,在__new__()方法中使用super()
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
解释:
1-尽管__new__()通常将调用类的引用作为其第一个参数,但在Python中它不是作为类方法实现的,而是作为静态方法实现的。也就是说,在直接调用__new__()时,对类的引用必须作为第一个参数显式传递:
# 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)
2-当调用super()来获取父类时,我们传递子类A作为它的第一个参数,然后传递对感兴趣对象的引用,在这种情况下,它是在调用A.__new__(cls)时传递的类引用。在大多数情况下,它也恰好是对子类的引用。在某些情况下,它可能不是,例如在多代继承的情况下。
super(A, cls)
3-因为作为一般规则__new__()是一个静态方法,super(a, cls)。__new__也将返回一个staticmethod,并且需要显式地提供所有参数,包括对感兴趣对象的引用,在本例中是cls。
super(A, cls).__new__(cls, *a, **kw)
4-不用super做同样的事情
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
在实例方法中使用super
例如,在__init__()中使用super()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
解释:
1- __init__是一个实例方法,这意味着它将实例引用作为第一个参数。当直接从实例调用时,引用会隐式传递,也就是说你不需要指定它:
# 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)
2-在__init__()中调用super()时,我们将子类作为第一个参数传递,感兴趣的对象作为第二个参数传递,后者通常是对子类实例的引用。
super(A, self)
3-调用super(A, self)返回一个代理,它将解析作用域并将其应用到self,就像它现在是父类的实例一样。因为__init__()是一个实例方法,所以s.__init__(…)调用将隐式地将self引用作为第一个参数传递给父类的__init__()。
4-要在不使用super的情况下执行同样的操作,我们需要将对实例的引用显式传递给父版本的__init__()。
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
在类方法中使用super
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()
2-当在类方法中调用super()以解析到父类的版本时,我们希望将当前子类作为第一个参数传递,以指示我们试图解析到哪个父类的作用域,并将感兴趣的对象作为第二个参数传递,以指示我们希望将该作用域应用到哪个对象,该对象通常是对子类本身或其子类之一的引用。
super(B, cls_or_subcls)
3-调用super(B, cls)解析到A的作用域并将其应用到cls。由于alternate_constructor()是一个类方法,调用super(B, cls).alternate_constructor(…)将隐式地将cls的引用作为第一个参数传递给a版本的alternate_constructor()
super(B, cls).alternate_constructor()
4-要在不使用super()的情况下实现同样的功能,您需要获得a .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)
上面的方法不起作用,因为A.alternate_constructor()方法将对A的隐式引用作为其第一个参数。这里传递的cls是它的第二个参数。
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)