1.如何使用super
一开始照着示例代码去用super的时候,觉得这个东西很简单,就像其他的单继承机制的语言中的super一样好用,好理解。可是,当你试着混合诸如bound super object,与unbound super object,metaclass,proxy object,descriptor,MRO,multiple inheritance 这样的概念一起来理解的时候,脑子就会变大,感觉这个东西后面的机制好复杂,一时半会真的很难弄清楚。
看了别人写的一些东西之后,尤其是是看了一个Michele Simionato(估计是Python领域一个大牛级别的人物)的三篇文章之后,对这个super才有了一点了解。附上,三篇文章的链接。
Things to Know About Python Super [1 of 3]
Things to Know About Python Super [2 of 3]
Things to Know About Python Super [3 of 3]
没错,作者从头到尾一直在吐槽,吐槽不同版本关于super的bug,指出一些设计的用例使用错误的情况。这其实,也是一个好的学习方式,学习别人发现的错误,自己尽量避免使用。
在交互终端,输入help(super)可以得到部分关于super的部分信息。
class super(object)
| super() -> same as super(__class__, <first argument>)
| super(type) -> unbound super object
| super(type, obj) -> bound super object; requires isinstance(obj, type)
| super(type, type2) -> bound super object; requires issubclass(type2, type)。
四中用法,个人感觉,第一种用法最为方便与安全,编译器会根据不同的情况自动的添加两个参数,在完全可以替代第三和第四两种,第二种方法,文档没有说明用例,有人说是用在其他类内做属性用的,暂时不是很理解。
requires isinstance(obj, type),obj,不一定是它的直接实例,子类的实例也可以,只有这样,obj不变,type参数一直变化(顺着type(obj)
.mro的顺序),才可能按照mro的顺序把所有祖先类中的方法调用一遍。
issubclass(type2, type)。type2与type可以一样。这种方法主要是用在调用祖先类的classmethod。
super对象充当的是”代理”对象,它的初始化信息,必然需要传入要代理的对象(type,obj)相关的信息,因为super要计算mro中下个祖先类),type来计算mro,obj作为祖先类方法的self参数。
至于,绑定和非绑定的super对象,可以借用java中的上转型对象来理解。
非绑定,得到的super对象,效果等同于祖先类,绑定的super对象,可以看做派生类的对象往上转型成祖先类的对象,super().属性名,或者super().属性名(),不一定调用的是计算出来的祖先类的方法,它还是在沿着mro搜索,调用搜索到的第一个方法或者属性。至于如何通过返回绑定的方法对象,可能是通过MethodType利用obj,以及搜索到的祖先类的方法对象,直接生成一个绑定的方法对象,(
- PyObject* PyMethod_New(PyObject *func, PyObject *self)
- Return value: New reference.
Return a new method object, with func being any callable object and self the instance the method should be bound. func is the function that will be called when the method is called. self must not be NULL.
),根据这个文档,我觉得这个推测应该是合理的。
class A(): def fun(self): print("AAAAA") class B(A): pass class C(B): pass cc = C() print("Print cc", cc) fun = super(C, cc).fun fun() print("print the object bounded to fun method\n", fun.__self__)
运行结果:
Print cc <__main__.C object at 0x00C0F5B0> AAAAA print the object bounded to fun method <__main__.C object at 0x00C0F5B0>
2.super的常见问题
1.调用的时候尽量不要使用,super(type(self), self),这会陷入无限递归之中。
2.参数传递问题,重写方法的时候,尽量用*args 与**kwargs
3.祖先类中的一些特性,比如【】运算,比如重写了__getattr__,但是super对象不能使用,毕竟只是个代理对象,不能完全实现祖先对象的全部功能。