使用 super 的正确方法(传递参数)

所以我跟随 巨蟒被认为是超级有害的,去测试他的例子。

然而,例子1-3应该显示在处理期望不同参数的 __init__方法时调用 super的正确方法,但是它不起作用。

这就是我得到的:

~ $ python example1-3.py
MRO: ['E', 'C', 'A', 'D', 'B', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B
Traceback (most recent call last):
File "Download/example1-3.py", line 27, in <module>
E(arg=10)
File "Download/example1-3.py", line 24, in __init__
super(E, self).__init__(arg, *args, **kwargs)
File "Download/example1-3.py", line 14, in __init__
super(C, self).__init__(arg, *args, **kwargs)
File "Download/example1-3.py", line 4, in __init__
super(A, self).__init__(*args, **kwargs)
File "Download/example1-3.py", line 19, in __init__
super(D, self).__init__(arg, *args, **kwargs)
File "Download/example1-3.py", line 9, in __init__
super(B, self).__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters

似乎 object本身违反了文档中提到的最佳实践之一,即使用 super的方法必须接受 *args**kwargs

现在,很明显 Knight 先生希望他的示例能够正常工作,那么在最近的 Python 版本中是否有所改变呢?我检查了2.6和2.7,两个都失败了。

那么处理这个问题的正确方法是什么呢?

187708 次浏览

正如在 Python 的 super ()被认为是 super中解释的那样,一种方法是让 class 吃掉它需要的参数,然后传递其余的参数。因此,当调用链到达 object时,所有的参数都被吃掉了,object.__init__将被调用而不带参数(正如它所期望的那样)。所以你的代码应该是这样的:

class A(object):
def __init__(self, *args, **kwargs):
print "A"
super(A, self).__init__(*args, **kwargs)


class B(object):
def __init__(self, *args, **kwargs):
print "B"
super(B, self).__init__(*args, **kwargs)


class C(A):
def __init__(self, arg, *args, **kwargs):
print "C","arg=",arg
super(C, self).__init__(*args, **kwargs)


class D(B):
def __init__(self, arg, *args, **kwargs):
print "D", "arg=",arg
super(D, self).__init__(*args, **kwargs)


class E(C,D):
def __init__(self, arg, *args, **kwargs):
print "E", "arg=",arg
super(E, self).__init__(*args, **kwargs)


print "MRO:", [x.__name__ for x in E.__mro__]
E(10, 20, 30)

如果需要大量的继承(这里就是这种情况) ,我建议您使用 **kwargs传递所有参数,然后在使用之后立即使用 pop(除非您在上层类中需要它们)。

class First(object):
def __init__(self, *args, **kwargs):
self.first_arg = kwargs.pop('first_arg')
super(First, self).__init__(*args, **kwargs)


class Second(First):
def __init__(self, *args, **kwargs):
self.second_arg = kwargs.pop('second_arg')
super(Second, self).__init__(*args, **kwargs)


class Third(Second):
def __init__(self, *args, **kwargs):
self.third_arg = kwargs.pop('third_arg')
super(Third, self).__init__(*args, **kwargs)

这是解决这类问题最简单的方法。

third = Third(first_arg=1, second_arg=2, third_arg=3)

有时两个类可能有一些共同的参数名。在这种情况下,不能从 **kwargs中弹出键值对或从 *args中删除它们。相反,您可以定义一个 Base类,它与 object不同,可以吸收/忽略参数:

class Base(object):
def __init__(self, *args, **kwargs): pass


class A(Base):
def __init__(self, *args, **kwargs):
print "A"
super(A, self).__init__(*args, **kwargs)


class B(Base):
def __init__(self, *args, **kwargs):
print "B"
super(B, self).__init__(*args, **kwargs)


class C(A):
def __init__(self, arg, *args, **kwargs):
print "C","arg=",arg
super(C, self).__init__(arg, *args, **kwargs)


class D(B):
def __init__(self, arg, *args, **kwargs):
print "D", "arg=",arg
super(D, self).__init__(arg, *args, **kwargs)


class E(C,D):
def __init__(self, arg, *args, **kwargs):
print "E", "arg=",arg
super(E, self).__init__(arg, *args, **kwargs)


print "MRO:", [x.__name__ for x in E.__mro__]
E(10)

产量

MRO: ['E', 'C', 'A', 'D', 'B', 'Base', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B

请注意,为了使其工作,Base必须是 MRO 中倒数第二个类。