还值得注意的是,适配可能是有损耗的。为了让一个对象去顺应一个接口,可能不方便或者不可能保持重新初始化这个对象所需要的所有信息。也就是说,通常情况下,对对象 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 的确定适配路径形成对照,我自己的模块执行了一个相对简单的推演,确定要分派的相关祖先类。不过两个库都倾向于在编程中鼓励使用类似的模块化思想 -- 由大量的小函数或类来执行“可插入的”任务,不需要受死板的类层级结构所困。在我看来,这种风格有其优越之处。
(编辑:aniston)
|