分类目录:《系统学习Python》总目录
自然地,之前的文章中的两个混合器父类变体都可以通过代码的一些额外变化来改进。除了两个值得简要关注的变体外,这里我们将略过大部分。首先,下面比较第一个混合方案的变种一一其使用了更加简单的编程结构,但是这也导致每次内置操作都增加一次额外调用,使得它更慢(尽管在一个代理上下文中慢得不是很显著):
class BulitinsMixin:def reroute(self, attr, *args, **kagrs):return self.__calss__.__getattr__(self, attr)(*args, **kargs)def __add__(self, other):return self.reroute('__add__', other)def __str__(self):return self.reroute('__str__')def __getitem__(self, index):return self.reroute('__add__', index)def __call__(self, *args, **kargs):return self.reroute('__add__', *args, **kargs)def accessControl(failIf):def onDecorator(aClass):class onInstance(BulitinsMixin):def __init__(self, *args, **kwags):self.__wrapped = aClass(*args, **kwags)def __str__(self):return str(self.__wrapped)def __add__(self, other):return self.__wrapped + otherdef __getitem__(self, index):return self.__wrapped[index]def __call__(self, *args, **kargs):return self.__wrapped(*args, **kargs)def __getattr__(self, attr):trace('get:', attr)if failIf(attr):raise TypeError('Private attribute fetch:' + attr)else:return getattr(self.__wrapped, attr)def __setattr__(self, attr, value):trace('set:', attr, value)f attr == '_onInstance__wrapped':self.__dict__[attr] = valueelif failIf(attr):raise TypeError('Private attribute change:' + attr)else:setattr(self.__wrapped, attr, value)return onInstancereturn onDecorator
其次,所有前置的内置混合类显式地编写了每个运算符重载方法,并截获了由运算发出的调用。有了替代代码,我们就能够从一个名称表单中机械地生成方法,并且通过创建类级别的描述符来仅仅截获调用前的属性获取一一正如在下面的代码中所做的,就像第二个混合替代方案,其假设被代理的对象在代理实例自身中被命名为_wrapped
:
class BulitinsMixin:class ProxyDesc(object):def __init__(self, attrname):self.attrname = attrnamedef __get__(self, instance, owner):return getattr(instance.wrapped, self.attrname)builtins = ['add', 'str', 'getitem', 'call']for attr in builtins:exec('__%s__ = ProxyDesc("__%s__")' % (attr, attr))
这一编程方法可能是最为简洁的,但也可能是最为隐晦和复杂的。它通过共享名称与其子类相当紧密地耦合在一起。这个类中最后的循环等价于下面在混合类的局部作用域中运行的语句一一一它通过从__get__
方法内的被包装对象获取属性,而不是捕获随后的操作调用自身,来创建响应初始名称查找的描述符:
__add__ = ProxyDesc('__add__')
__str__ = ProxyDesc('__str__')
通过内联或混合继承添加了这样的运算符重载方法后,前面使用__add__
和__str__
重载了+
和print
的Private
示例客户端在Python2.X和Python3.X下都能正确地工作,重载了索引和调用的子类也一样工作。
参考文献:
[1] Mark Lutz. Python学习手册[M]. 机械工业出版社, 2018.