正确的方式使用超级(parameter passing)
所以我跟着Python的Super Considered Harmful ,去testing他的例子。
但是, 示例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,两者都失败。
那么解决这个问题的正确方法是什么呢?
有时两个类可能有一些共同的参数名称。 在这种情况下,您不能从**kwargs
popup键值对,或者将它们从*args
移除。 相反,您可以定义一个不同于object
的Base
类,吸收/忽略参数:
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中倒数第二个类。
如果你要有很多的inheritance(这里就是这种情况),我build议你使用**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)
正如在python 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)