Python 是一种相当高级的语言,但是我们为什么只是局限于这一级别的抽象呢?利用 PEAK(Python Enterprise Application Kit),我们可以实现更高级的抽象。
PEAK 是 TransWarp 的后继者,是一个用 Python 开发的用于软件自动化的实验性工具包,是一个用于快速开发和重用应用程序组件的 Python 框架。PEAK 为您提供一个组件架构、组件基础设施,以及各种用于构建应用程序的通用组件和组件框架。
介绍 PEAK 最简单的方法是作为“Phillip J. Eby 最近研究的最疯狂的思想”。不管语气如何,我对此总是有些调侃。尽管 PEAK 已经吸引了像其他中等规模的 Free Software Python 项目一样多的贡献者,但是实际上 PEAK 的方向是由不断变革的目标和最初创建者的兴趣所驱动的。
PEAK 随着这种不断变化的兴趣而变化的一个必然结果是,它在可预见的未来一段时间内将可能有点是“实验性的”。这就是说,我们不用太过担心这个问题 —— 我所尝试过的每个 PEAK 版本都很稳定,而且都提供了一些特性。另外,您现在可以获得 PEAK 最新快照的一个自动更新的 tarball,其中还提供了一个非常友好的 distutils
安装脚本。
从我最后一次介绍 PEAK 到现在的一年时间中(请参阅 可爱的 Python: Python Enterprise Application Kit),PEAK 中所引入的最有趣的一种思想是通用函数(generic function)。本文将重点介绍这种功能,虽然这不过是整个 PEAK 的冰山一角。而且,这种思想可以与我的多分派模块(multimethods
)很自然地结合起来,我很高兴看到 PEAK 可以对分派风格进行扩展。
在开始继续讨论通用函数之前,我们有必要先来看一下 PEAK 的 Wiki 主页(您可以查看这个页面的最新状态,请参阅 参考资料 中的链接)上给出的一个 PEAK 的组件图。
一点简短的提示:术语 “predicate dispatch”比“predicative”使用得更多,尽管后者从文法上来说更加适合。如果我们要在 Web 或者库函数中进行搜索,可以尝试一下这个简短的拼写方法。
阅读过我之前编写的有关 Gnosis Utilities 模块 gnosis.magic.multimethods
的文章的读者对于多分派都有一个基本的印象了。现在我们回忆一下,大部分 OOP 编程都是单一的分派;这就是说,只有一个指定的对象用来确定要走哪条代码路径。在一个诸如 foo.doIt(other,args,here)
之类的调用中,点号之前的参数的类 —— 也就是 Foo
—— 确定了要运行哪些代码;other
等参数的类型可能会在 Foo.doIt()
的 if
语句中进行测试,但是不会直接影响代码的分派。
从概念上来说,一种更加通用的技术是让函数/方法的所有参数都以相同的度量来确定自己的专有程度。在多分派的系统中,诸如 doIt()
之类的通用函数可以专门用来处理各种专用的类型签名。在 gnosis.magic.multimethods()
API 中,这可能会类似于清单 1 所示:
1
2
3
4
|
doIt.add_rule((Foo1, Other2, int), func1)
doIt.add_rule((Foo2, Other1, str), func2)
doIt.add_rule((Foo1, Other1, float), func3)
doIt(foo, other, args) # 'foo' is just one co-equal specializer
|
PEAK 的 dispatch
模块也有一个基于类型的分派器,但是目前尚微不足道,因为它只能处理单一分派 —— dispatch.on()
封装程序除了基于普通 Python 类的分派之外还做不了多少事情。尽管如此,看到 PEAK 的基于类型的分派仍然让我们对完整断言分派的语法兴奋不已。注意这些例子都利用了 Python 2.4 中新的 decorator 语法,用于修改所定义的函数或方法。您可以在早期版本的 Python 中使用 PEAK 的通用函数,但是其语法并不美观(如清单 2 所示):
1
2
3
4
5
6
7
8
9
10
11
12
|
import dispatch
@dispatch.on('foo')
def doIt(foo, other, args):
"Base generic function of 'doIt()'"
@doIt.when(int)
def doIt(foo, other, args):
print "foo is an int |", other, args
@doIt.when(str)
def doIt(foo, other, args):
print "foo is a str |", other, args
doIt( 1, 'this','that') # -> foo is an int | this that
doIt('x','this','that') # -> foo is a str | this that
|
对于新的类型来说,的确可以添加 dispatch.on()
通用函数签名,而不用修改以前的代码。例如,我可以向前面的代码中添加 @doIt.when(float)
或 @doIt.when(MyClass)
;如果需要,以后的调用就可以利用它。但是即使不用 PEAK dispatch
包,也有很多方法可以实现同样的功能。
商务合作,请加微信:Fun-lying