Python Cookbook-6.20 精确和安全地使用协作的超类调用
任务
你很欣赏通过内建的super 来支持的多继承的代码的协作方式,但同时你也希望能够更加简洁和精确地使用这种方式。
解决方案
一个好的方案是使用支持多继承的 mixin 类,此类使用了内省机制,且更加简洁:
import inspect
class SuperMixin(object):def super(cls,*args,**kwargs):frame = inspect.currentframe(1)self = frame.f_locals['self']methodName = frame.f_code.co_namemethod = getattr(super(cls,self), methodName, None)if inspect.ismethod(method):return method(*args,**kwargs)super = classmethod(super)
任何从 SuperMixin 派生的类都将获得一个名为 super 的魔术般的方法:在 cls 类的一个叫做 somename 的方法中调用 cls.super(args)等于是在调用 super(cls,self).somename(args)。而且,即使在方法解析顺序(Method Resolution Order,MRO)中,在 cls 类之后没有任何类定义过一个叫做 somename 的方法,这个调用也是安全的。
讨论
下面是一个用法示例:
if __name__ = '__main__'class TestBase(list,SuperMixin):#注意:myMethod并未在此定义passclass MyTest1(TestBase):def myMethod(self):print "in MyTest1"MyTest1.super()class MyTest2(TestBase):def myMethod(self):print "in MyTest2"MyTest2.super()class MyTest(MyTest1, MyTest2):def myMethod(self):print "in MyTest"MyTest.super()MyTest().myMethod()
#输出:
# in MyTest
# in MyTest1
# in MyTest2
Python 已经支持“新风格”类好些年了,作为比经典类更好的选择,新风格类也是默认的选项。经典类的存在仅仅是为了满足一些老版本 Python 向后兼容的需要,对于新代码是不推荐使用的。使用新风格类的一个优点就是,我们可以很轻松地以一种“协作”的方式调用超类的实现,而且还完全支持多继承,这应当归功于内建的super。
假设你的新风格类 cls 中有一个方法,这个方法需要先完成自己的任务然后将余下的工作委托给超类的同名方法。代码一般为:
def somename(self,*args):...some preliminary task...return super(cls,self).somename(*args)
这个做法有两个小问题:比较啰嗦,而且它依赖于超类提供了方法somename。如果你想把 cls 和其他类隔离得更开一些,从而获得更强的健壮性,可以除掉其依赖性,但代码就变得更啰嗦了:
def somename(self,*args):...some preliminary task...try:super_method = super(cls,self).somenameexcept AttributeError:return Noneelse:return super_method(*args)
而解决方案中的 mixin 类 SuperMixin 则完美解决了这两方面的问题。只要 cls 直接或间接地从 SuperMixin 派生(也可以同时从其他需要的类派生),你就能够以一种精确和健壮的方式完成任务:
def somename(self,*args):...some preliminary task...return cls.super(*args)
类方法 SuperMixin.super 依赖于一个简单的内省来获取self对象以及方法的名字,然后在内部使用内建的super和getattr 来获取超类方法,如果此方法存在的话,再安全地调用之。内省是通过 Python 标准库的方便的inspect模块实现的,这使得整个任务变得更加简单。