X
首页 Cheetah技术专栏 使用 Python 和 Cheetah 构建和扩充模板(1)
使用 Python 和 Cheetah 构建和扩充模板(1)

阅读本文之后,您就能够使用 Python 脚本和 Cheetah 模板生成任何种类的基于文本的内容。Cheetah 模板易于了解和维护,并且可以帮助您将文档的静态部分与动态部分分离。
一个过于臃肿的模板系统

“使用 SQLObject 连接数据库与 Python”中提到各种用于 Python 的开源对象关联式映射库。Python 编程人员喜欢按照自己的方式办事,这带来了许多附属成果。不过,所有这些努力常常汇集成一个对所有人都非常有益的包。

同样的模式对模板系统却已经过时:表示静态文本的方式将像窗体一样被扩充,这样可以随后插入动态元素。官方的 Python Wiki 链接了近 20 个模板系统,这些只是一些主要的模板系统。还有更多的模板系统,随 Python 一起打包的是一些基本的模板系统,这些模板系统将在简单的情形下工作。

本文将描述模板系统可以解决的问题。还将描述 Cheetah,它是已经设计出来的最好的 Python 模板系统。本文假定您具备 Python 方面的基础知识,但不具备模板系统及其用处方面的知识。

基本的模板概念

假定您正在为一家在线商店编写一个 Web 应用程序。您需要一些类来表示这家商店和购买过程的某些方面:库存物品、客户和客户所下的定单,等等。这些类的实例可能对应于数据库中的行,并且它们被用来

为什么要使用模板系统?

如果没有模板系统,那么就要使用 Python 代码生成类似示例电子邮件消息的文本片段。还要编写附加到字符串列表的逻辑或为类文件对象编写的逻辑。例如,以下代码可以生成上面所述的电子邮件:

清单 3. 生成电子邮件
from DummyObjects import dummyUser, dummyOrder
l = []
l.append('Hello, ')
l.append(dummyUser.firstName)
l.append('.\n\nYour order (#')
l.append(str(dummyOrder.id))
l.append(') has shipped:\n')
for purchased, quantity in dummyOrder.purchased.items():
     l.append(' ')
     l.append(purchased.name)
     l.append(': ')
     l.append(str(quantity))
     l.append(' unit(s)\n')
l.append('\nYour tracking number is ')
l.append(dummyOrder.trackingNumber)
l.append('.')
print ''.join(l)
 

不幸的是,这个代码不像它的输出那样好。通过查看此代码,难以看出电子邮件消息的结构。许多代码是重复的(例如,有许多对列表的 append() 方法的调用),这为错误创造了空间。最后,团队的 UI 设计人员可能宁愿编辑类似上面描述的抽象模板那样的东西,也不愿意编辑这种 Python 代码。由于上述这些原因,开发人员转向模板系统。

有 Python 的内置模板系统就足够了吗?

Python 提供了少许内置模板系统,它们在简单情况下工作得很好。很长时间以来,Python 一直拥有一些简单的模板系统,它们有一些易于让人理解的格式,这些格式令人回想起 C 的 printf() 字符串格式:


清单 4. Python 的内置模板系统
from DummyObjects import dummyUser, dummyOrder
print 'Hello, %s.\n\nYour order (#%d) has shipped:' % (dummyUser.firstName,
                                                       dummyOrder.id)
print 'Hello, %(firstName)s.\n\nYour order (#%(orderID)d) has shipped:' % \
{'firstName' : dummyUser.firstName, 'orderID' : dummyOrder.id}
 

Python V2.4 引入了一个具有看起来更现代的格式的模板系统。变量名称是通过添加美元符号($)作为前缀来指定的;这类似于 Perl、PHP、大多数 shell 语言和 Cheetah:

清单 5. Python V2.4 的内置模板系统
from string import Template
from DummyObjects import dummyUser, dummyOrder
t = Template('Hello, $firstName.\n\nYour order (#$orderID) has shipped:')
t.substitute({'firstName' : dummyUser.firstName, 'orderID' : dummyOrder.id})
 

这些模板系统都有两个重要的缺点。

它们的模板定义都不能调用任何方法,或者访问 dummyUser 和 dummyOrder 对象的任何成员。不能将 dummyUser.firstName 放入模板定义中,必须将它放入应用于模板定义的映射中。所有将插入静态模板定义中的动态信息都必须首先拆分为基本的 Python 数据类型。
这些模板系统没有流控制 —— 没有循环或条件。以前的那些例子恰好在必须按顺序在项上进行迭代之前停止执行,并且有一个好的理由:它们使用的模板系统无法在模板定义内部进行这种迭代。您需要编写 Python 代码在这些项上按顺序进行迭代,将多个字符串连接在一起(可能使用中间模板),并以称为 itemsOrdered 的单个字符串的形式向模板系统提供最终结果。循环本身是电子邮件主体的静态部分的一部分 —— 无论处理什么样的用户和定单,其工作方式是相同的 —— 但无法将它应用到静态模板定义中。
多数可用于 Python 的附加模板系统试图弥补这两个缺点。其中最好的替代是 Cheetah。

<下一页>