Python 模板引擎

6 投票
3 回答
12113 浏览
提问于 2025-04-15 23:26

有没有人能帮我入门,教我怎么写一个Python模板引擎?我刚开始学Python,已经写了一个简单的MVC框架,并在自己的轻量级WSGI服务器上运行。

我写了一个脚本,可以找到并替换键值对:
(当然,这不是我脚本的实际结构或实现方式,这只是一个例子)

from string import Template

html  = '<html>\n'
html += '  <head>\n'
html += '  <title>This is so Cool : In Controller HTML</title>\n'
html += '  </head>\n'
html += '  <body>\n'
html += '    Home | <a href="/hi">Hi ${name}</a>\n'
html += '  </body>\n'
html += '<html>'

Template(html).safe_substitute(dict(name = 'Arturo'))

我接下来的目标是实现自定义语句、修饰符、函数等等(比如'for'循环),但我不太确定是否需要使用我不知道的其他模块。我想过使用正则表达式,但感觉那样做效率不高。

任何帮助都非常感谢,我相信这对其他人也会有用。

谢谢!

3 个回答

1

好吧,当我决定像你一样尝试的时候,测试驱动开发(TDD)总是个不错的选择。

那么,为什么不试试呢?

比如,创建一个叫做 jturo_template.py 的文件,然后写:

import re
import unittest

class JTuroTemplate(object):
    u"""JTuro's template engine core class"""
    variable_regex = r'\$\{((.*)(%s)([^}]*))*\}'
    def __init__(self, string):
        self.string = string

    def __repr__(self):
        pieces = self.string.split()
        if len(pieces) > 3:
            head = "%s ..." % " ".join(pieces[:3])
        else:
            head = " ".join(pieces)

        return u'<JTuroTemplate: "%s">' % (head)

    def render(self, context):
        new = unicode(self.string)

        for key, value in context.items():
            variable_name = re.escape(key)
            regex = re.compile(self.variable_regex % variable_name)

            for match in regex.findall(new):
                if match[0]:
                    replacement = match[0].replace(key, repr(value))
                    new = new.replace('${%s}' % match[0], unicode(eval(replacement)))

        return new

class TestJTuroTemplate(unittest.TestCase):
    def test_repr(self):
        "a instance will be nicely represented"
        jt = JTuroTemplate('my template')
        self.assertEquals(repr(jt), '<JTuroTemplate: "my template">')

    def test_repr_truncated(self):
        "the python representation is truncated after 3 words"

        jt = JTuroTemplate('such a long string template')
        self.assertEquals(repr(jt), '<JTuroTemplate: "such a long ...">')

    def test_solves_simple_variables(self):
        "it solves simple variables"

        jt = JTuroTemplate('my variable is ${var} == 4')
        self.assertEquals(jt.render({'var': '4'}), 'my variable is 4 == 4')

    def test_solves_variables_with_python_code(self):
        "it solves variables with python code"

        jt = JTuroTemplate('my variable is ${var + var} == 44')
        self.assertEquals(jt.render({'var': '4'}), 'my variable is 44 == 44')

if __name__ == '__main__':
    unittest.main()

抱歉发了这么长的帖子,但我觉得你可以试试这个工作流程:

  1. 先写一个会失败的测试
  2. 运行它,看看它失败了
  3. 写代码让这个测试通过
  4. 再运行一次,看看它通过了
4

好吧,正如你所说,你是个Python新手,而你之所以从头开始写一个新的MVC框架和模板引擎,纯粹是为了学习,这样的话,我觉得你不需要太在意性能。

不过,如果你打算把它用在实际项目中,那就应该考虑使用一些已经存在的模板引擎,比如jinja2、mako、genshi等等。

无论如何,如果你想玩玩的话,这里有一个轻量级的Python网页框架的好例子:http://github.com/breily/juno

还有一个快速的模板引擎可以看看:http://github.com/eklitzke/spitfire

祝你编程愉快!

20

Python支持很多强大的模板语言。我个人比较喜欢Jinja2。你也可以看看MakoGenshi

Mako在这三者中速度最快,但它的设计理念允许在模板中写复杂的代码逻辑,这有时会违反MVC原则。

Genshi的概念非常出色,特别是它的一个强大功能是反向模板继承。但在这三者中,它的速度是最慢的,实际使用中它的很多功能往往显得过于复杂。

在我个人看来,Jinja2是一个很好的折中选择。它容易扩展,速度也不错,而且很多人都比较熟悉,因为它的语法和Django模板以及Liquid模板语言类似。

撰写回答