当调用super()
以解析父类的classmethods、instance methods或staticmethod时,我们需要将当前所在范围的类作为第一个参数传递,以指示我们尝试解析哪个父类的范围,并且作为第二个参数传递感兴趣的对象,以指示我们正在尝试应用该范围到哪个对象上。
考虑一个类层次结构A
,B
和C
,其中每个类都是其后面的类的父类,a
,b
和c
分别是它们的实例。
super(B, b)
super(C, c)
super(B, c)
如何在staticmethod中使用super()
例如,在__new__()
方法中使用super()
。
class A(object):
def __new__(cls, *a, **kw):
return super(A, cls).__new__(cls, *a, **kw)
解释:
1- 即使通常情况下__new__()
的第一个参数应该是调用类的引用,但在Python中它并没有作为类方法来实现,而是作为静态方法来实现。也就是说,在直接调用__new__()
时,必须明确地传递一个类的引用作为第一个参数:
class A(object):
def __new__(cls):
pass
A.__new__()
A.__new__(A)
2- 当调用 super()
来获取父类时,我们将子类 A
作为其第一个参数传递,然后我们传递一个感兴趣的对象的引用,即在调用 A.__new__(cls)
时传递的类引用。在大多数情况下,这也恰好是对子类的引用。但在某些情况下可能不是,例如在多代继承的情况下。
super(A, cls)
3- 由于通常情况下__new__()
是一个静态方法, super(A, cls).__new__
也会返回一个静态方法,因此需要显式地提供所有参数,包括感兴趣的对象的引用,即cls
。
super(A, cls).__new__(cls, *a, **kw)
4- 在没有使用super
的情况下执行相同的操作
class A(object):
def __new__(cls, *a, **kw):
return object.__new__(cls, *a, **kw)
使用super
调用实例方法
例如,在__init__()
中使用super()
class A(object):
def __init__(self, *a, **kw):
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
,就好像它现在是父类的实例。让我们称这个代理为 s
。由于 __init__()
是实例方法,调用 s.__init__(...)
将隐式地将 self
的引用作为第一个参数传递给父类的__init__()
。
4- 如果没有使用super
,我们需要显式地向父类版本的__init__()
传递一个实例的引用来完成同样的操作。
class A(object):
def __init__(self, *a, **kw):
object.__init__(self, *a, **kw)
使用 super
处理 classmethod
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):
print "B.alternate_constructor called"
return super(B, cls).alternate_constructor(*a, **kw)
解释:
1- 类方法可以直接从类调用,并将其第一个参数作为对类的引用。
a = A.alternate_constructor()
b = B.alternate_constructor()
2- 当在一个classmethod中调用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):
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):
print "B.alternate_constructor called"
unbound_func = A.alternate_constructor.im_func
return unbound_func(cls, *a, **kw)
list
接口的另一个实现,比如doublylinkedlist
,那么应用程序可以平稳地使用它。我可以通过引入config.txt
并在加载时链接实现来使我的示例更具可配置性。这是正确的示例吗?如果是,我该如何将您的代码与之相关联?请参见维基百科中DI的第一个优点。在您的代码中,任何新的实现都是可配置的。 - overexchange__init__
函数会出现一些复杂情况,特别是当类层次结构中的类之间的__init__
签名不同时。我已经添加了一个关注这个方面的答案。 - Aviad Rozenhekself
(例如没有隐式对象变量解析)。 - Roman Shapovalov