在 Python 中对协议使用 PEAK
2007-03-30 12:25:16 来源:WEB开发网adapt() 的假想实现
if isinstance(component, protocol):
return component
elif hasattr(component,'__conform__'):
return component.__conform__(protocol)
elif hasattr(protocol,'__adapt__'):
return protocol.__adapt__(component)
elif default is not None:
return default
elif factory is not None:
return factory(component, protocol)
else:
NotImplementedError
对 adapt() 的调用 应该保持一些特性(不过这是对程序员的建议,而不是库的一般强制要求)。对 adapt() 的调用应该是等幂的。也就是说,对于一个对象 x 和一个协议 P ,我们希望: adapt(x,P)==adapt(adapt(x,P),P) 。高级地,这样做的目的类似于从 .__iter__() 方法返回自身( self )的迭代器(iterator)类的目的。您基本上不会希望去重新适配到您已经适配到的相同类型以产生波动的结果。
还值得注意的是,适配可能是有损耗的。为了让一个对象去顺应一个接口,可能不方便或者不可能保持重新初始化这个对象所需要的所有信息。也就是说,通常情况下,对对象 x 及协议 P1 和 P2 而言: adapt(x,P1)!=adapt(adapt(adapt(x,P1),P2),P1) 。
在结束之前,让我们来看另一个利用了 adapt() 的低层次行为的测试脚本:
test_lispy2.py 对象序列化
from lispy import *
class Bar(object):
pass
class Baz(Bar):
def __repr__(self):
return "Represent a "+self.__class__.__name__+" object!"
class Bat(Baz):
def __conform__(self, prot):
return "Adapt "+self.__class__.__name__+" to "+repr(prot)+"!"
print adapt(Bar(), ILisp)
print adapt(Baz(), ILisp)
print adapt(Bat(), ILisp)
print adapt(adapt(Bat(), ILisp), ILisp)
$ python2.3 test_lispy2.py
<__main__.Bar object at 0x65250>
Represent a Baz object!
Adapt Bat to WeakSubset(<type 'unicode'>,('__repr__',))!
'(Adapt Bat to WeakSubset(<type 'unicode'>,('__repr__',))!)
结果证明 lispy.py 的设计不能满足等幂的目标。改进这一设计可能是个不错的练习。不过,像 ILisp 这样的描述肯定会损耗原始对象中的信息(这是没关系的)。
结束语
感觉上,PyProtocols 与本专栏提及的其他“外来”话题有一些共同之处。首先,声明 API 是声明性的(相对于解释性)。声明性编程并不给出执行一个动作所需要的步骤和开关,而是声明处理特定的内容,由库或编译器来具体指出如何执行。名称“declare*()”和“advice*()”正在来自于这一观点。
不过,我也发现 PyProtocols 编程有些类似于使用多分派进行编程,具体说就是使用我在另一期文章提到的 gnosis.magic.multimethods 模块。与 PyProtocols 的确定适配路径形成对照,我自己的模块执行了一个相对简单的推演,确定要分派的相关祖先类。不过两个库都倾向于在编程中鼓励使用类似的模块化思想 -- 由大量的小函数或类来执行“可插入的”任务,不需要受死板的类层级结构所困。在我看来,这种风格有其优越之处。
更多精彩
赞助商链接