Python模板引擎

6

有人能帮我开始编写 Python 模板引擎吗?我的 Python 技术还不够熟练,但我已经写了一个轻量级的类 WSGI 服务器来运行自己的小型 MVC 框架。

目前我已经编写了一个脚本来查找和替换键值对:

(显然这并不是我实际脚本的结构或实现方式,只是一个示例)
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”循环),但我不确定是否应该使用我不知道的另一个模块。我考虑过正则表达式,但感觉这不是一种高效的方法。
欢迎任何帮助,我相信这对其他人也会有用。
谢谢。

看看Django:http://code.djangoproject.com/。特别是,要查看这些内容:http://code.djangoproject.com/svn/django/trunk/django/template/。 - Adam
我并不想听起来很无礼,但是既然已经有大量现成的框架可用,为什么还要自己写框架呢?迄今为止,Django是最受欢迎的框架;如果你只是为了了解它的工作原理,我会推荐你去看看。 - Sasha Chedygov
@Adam 你发的链接(第二个)很棒,看看这个领域的巨头们是怎么做的总是好的。是时候让我开始学习了。 - jturo
2
@musicfreak 你一点也不显得粗鲁,事实上,那是一个非常有价值的观点。 选择已经编写好的框架是最合乎逻辑的选择。编写自己的框架是我学习语言的方式,此外,有很多事情发生在幕后,对于像我这样(不是专家使用Python)的人来说,可能无法识别Django魔法和Python魔法之间的区别。 - jturo
好的,这是一个很好的理由。 :) - Sasha Chedygov
3个回答

20

Python支持许多强大的模板语言。我更喜欢 Jinja2。还可以看看 MakoGenshi

Mako是三种中最快的,但它的理念允许在模板中拥有复杂的代码逻辑,这有时会违反MVC原则。

Genshi的概念很好,尤其是倒置模板继承的强大功能。但它是三种中最慢的,而实践表明,它所有的功能在真实项目中经常被过度使用。

依我个人的看法,Jinja2是一个黄金平衡点。它易于扩展、相当快速,并且对许多人来说很熟悉,因为它使用的语法类似于Django模板和Liquid模板语言。


感谢你的帮助,nailxx。Mako网站显示了以下信息:Mako:1.10ms,Myghty:4.52ms,Cheetah:1.10ms,Genshi:11.46ms,Django:2.74ms,Kid:14.54ms,这很酷,他们花时间比较了引擎。我的记忆能力非常有限,因此我使用约定来帮助我解决这个问题。只要不是业务逻辑(我不知道你所说的“复杂代码逻辑”是否指的是业务逻辑),我就可以在模板中使用代码逻辑。 - jturo

4

你说你是一个Python新手,而且唯一的原因是为了学习而从头开始编写一个新的MVC框架和模板引擎,那么我认为你不必关注性能。

但是如果你决定投入生产,应该考虑使用已经存在的模板引擎,如jinja2、mako、genshi等。

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

还有一个lightspeed模板引擎:http://github.com/eklitzke/spitfire

祝你愉快!


嗯,我必须承认你说得有道理。感谢提供链接和脚本。这是很好的东西可以拿来玩耍和学习。 - jturo

1

好的,当我像你一样决定玩耍时,测试驱动开发始终是一种不错的方式。

那么,为什么不试试呢?

例如,创建一个名为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. 再次运行并观察其通过

谢谢你的代码,这也需要一些学习。我认为正则表达式是一种解决方法,但是一个拥有大量流量的网站使用模板引擎执行大量正则表达式会变得非常缓慢,这是个大问题。再次感谢Gabriel的帖子。 - jturo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接