Fate

(500 lines or less)利用python做一个模板引擎(上)

Markdown

500 lines or less 是github上一个有14000赞的代码仓库,包含多个小项目,非常适合Python以及其他语言的入门者和进阶者学习.

前言

  • 我这里所写的是自己学习这个项目之后的所感所想,同时也对该文档做一个精简版的说明,如果想详细了解该项目的细节,请移步中文文档

  • 有时候我们写以文字为主的文件(比如HTML)时,里面大部分都是静态的,但是还是有小部分的文件是动态数据,比如我们想生成这样的一份数据,Hello后面的用户名以及下面的书籍列表是动态生成的:

    1
    2
    3
    4
    5
    6
    <html>
    <h1>hello!Lee</h1>
    <p>book1</p>
    <p>book2</p>
    <p>book3</p>
    </html>
  • 我们自然就会用一个这样的函数来处理这些传入的数据来生成HTML

    1
    2
    3
    4
    5
    6
    def make_page(name,book_list):
    PAGE = "<html>\n<h1>hello!%s</h1>\n"%name
    for book in book_list:
    PAGE += "<p>%s</p>\n" % book
    PAGE+="</html>"
    return PAGE
  • 但是这样做是很麻烦的,不同的页面需要不同的代码,而且将HTML的静态文本分成一块一块的,非常难改动,特别是当页面需要进行变动的时候.

模板引擎

  • 概念

    模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档(引自百科)。

  • 如果大家学过Java Web或者别的Web框架会了解模板引擎的概念.将静态文本中的动态数据进行某种标注后,整个模板就会交给后台的逻辑处理代码生成标准的HTML文档(MVC设计模式?).所以模板引擎是以静态文本为主,中间将动态数据用特殊的符号标记,将这份模板传入引擎处理后,模板将变成python代码,然后执行生成最终的静态文本.

  • 综上所述,模板引擎其实就是将我们生成代码的思路从以逻辑为主变成以文本为主.接下来,我们做一个简单的模板引擎.

模板引擎所支持的语法

  • 由于是一个最初始的模板引擎,所以我们只提供如下语法支持 (打’{‘老报错,换成’(‘,估计hexo把我这当模板给翻译了 @^@):

    1.(( name|filter1|filter2|..)) : 其中包含的是上下文变量以及过滤器,每一个元素以管道分割,比如说 ((name|upper)) 那么在python代码中就会以upper(name)实现.

    2.(%…%): 提供条件判断语句以及循环语句的支持。条件语句和循环语句分别以(%endif%)、(%endfor%)结尾.

    3.点操作 : 点操作将有可能出现1和2中,点操作有可能是以下三个操作中的一个.

    • x[‘y’] : 表示 x 是一个字典,y是其中的一个键.
    • x.y : 表示 y 是 x的一个属性.
    • x() : 表示x是一个可调用(callable)的方法.代码将自动调用.

    4.(#…#) : 注释语句,会被翻译成为 <!–…–>.

  • 使用我们的模板将上面的html代码变成模板:
    1
    2
    3
    4
    5
    6
    <html>
    <h1>Hello {{name}}!</h1>
    {% for book in book_list %}
    <p>{{book}}</p>
    {% endfor %}
    </html>

引擎所生成的python代码模板

  • 我们将使用使用引擎生成如下python代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def render_function(context, do_dots):
    # 变量名
    result=[]
    append_result = result.append
    extend_result = result.extend
    to_str = str
    # 模板所生成的python代码
    append_result('\n ')
    return "".join(result)
  • 上述python代码总的来说就是一个方法,其包含两个参数contextdo_dots,context是传入的字典,字典中包含的是HTML中所需要的变量名和变量值,do_dots是处理HTML代码中的点操作的函数.

  • 所有传入的变量名都会以c_开头,以保证不会与python内置方法发生冲突.

  • 还有一个微优化,就是append_result=result.append,这里引用原作者的话:”在模板引擎代码中我们用这种分离的方式是的我们不论做多少次第二步,只用做一次第一步。这节省了我们少量的时间,因为避免了再花时间去查找对象的append属性。”,又学一招(笑)

  • 我们将上面的html模板通过引擎将生成如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def render_function(context, do_dots):
    c_name = context['name']
    c_book_list = context['book_list']
    result=[]
    append_result = result.append
    extend_result = result.extend
    to_str = str
    extend_result(['\n <h1>Hello ',to_str(c_name),'!</h1>\n '])
    for c_book in c_book_list:
    extend_result(['\n <p>',to_str(c_book),'</p>\n '])
    append_result('\n ')
    return "".join(result)
  • 在下一篇博客我们将讨论代码的具体实现.

参考博客

从零开始一个模板引擎的python实现——500 lines or less-A Template Engine翻译(上)

热评文章