python实现泛型函数的简单介绍

各位有没有详细讲Python泛型的资料我们开始真正地讨论Python3000了 。这里有一个新的邮件列表和一个版本分支 。首要的问题是关于流程的 。Python 增强建议书(Python Enhancement Proposal,简称PEP)的很多新格式正在制定,目的是为了避免重蹈Perl 6的覆辙:-) 。我在blog一个关于功能的提案,这个提案在过去一段时间里已经发生了很大的变化)
自盘古开天地之日起,Alex Martelli就一直是配接的忠实拥护者 。他经常埋怨我对配接的光芒视而不见 。现在,我为自己当时的不开窍而感到庆幸 。
我先用最简单的形式来介绍一下配接吧 。这个想法诞生于一种常有的情况python实现泛型函数:需要用到对象包装器(object wrapper)[贴切地命名为配接器模式(Adapter Pattern)] 。PEP246打算提供一个内置的函数adapt(X, P),X可以是任何对象,而P也可以是任何Protocol 。我们故意不对protocol进行定义,只要它可以通过对象表现出来就可以 。调用adapt(X, P)返回一个由X构建并满足P的对象 , 如果创建对象失败 , 则抛出一个异常 。它使用全局注册表(global registry)为配接器功能提供了类型和protocol之间的映射关系 。我们可以写为dict R = {(T, P): A, ...} 。然后,adapt(X, P) 计算出adapter A = R[type(X), P],并返回A(X) 。还有一个注册函数register(T, P, A),它简单地设置 R[T, P] = A 。请参见Alex更为精彩的解释,他补充了很多我遗漏掉的东西 。
当Alex提出他对配接工作原理的这一看法 , 好几个人(包括我自己在内)都意识到全局注册表是没有必要的 。每个protocol都可以有自己的注册表(registry) 。所以,现在我们在protocol上使用adapt()和register()方法 。我们使用P.adapt(X)而非adapt(X, P),使用P.register(T, A)而非register(T, P, A) 。A的签名(signature)保持不变 。我称之为第二代配接(second-generation adaptation) 。
这样做的好处是python实现泛型函数你们再也不用局限于一种固定的全局的register()和adapt()实现 。Alex提到了很多他忽略的问题,但是如果要真正实现,这些问题需要得以解决 。例如,如何处理对象类型未被注册而一些基本类型已被注册的配接 , protocol之间的继承是如何定义的(当你把protocol和接口等同起来时会很有用,就像Zope和Twisted一样),对象已实现protocol/接口时的自动检测(这在Zope和Twisted中有用) 。一些扩展(extension)有在查找(lookup)的性能上有问题,我们可以通过几种不同的方法来解决 。通过多重协议实现(multiple protocol implementations)(每次都实现相同的adapt()和register() APIs),每个框架(framework)都对配接如何为其自身拥有的protocol服务有自己的主张,而没必要使用一个固定的全局实现 。对于一个特定的框架而言,配接的全局实现可能达到最佳效果,但也未必就是最好的选择 。
Ian Bicking提出了一个对立的观点:我们为什么不使用泛型函数而非配接呢?他和Phillip Eby都认为泛型函数具有的功能比配接器更强大,至少在某种程度上差不多 。现在我就来简要说一下泛型函数 。
一个泛型函数G,可以被调用 , 这种行为类似一个普通函数(取参数并返回一个值),但其实现是可扩展的(extensible),并可以在不同的模块中进行定义 。TG包含一个由复合类型参数的元组索引的注册表实现 。假设我们想让具有两个参数的G可调用 , 那么注册表将会把成对了类型组(type pairs)映射到实现的函数中 。我们可以使用G.register((T1, T2), F) 来显示地定义,当type(X1)==T1、type(X2)==T2时 , F(X1, X2)是G(X1, X2)的合适的实现 。最简单的实现就是把参数映射到它们的类型(类或许更好),转换为元组,并利用它作为注册表的键值来找到实现函数 。如果没有找到健值,就调用缺省实现,前提是预先定义G,并提供一些回调或抛出一个异常 。

推荐阅读