让我们一起来构建一个模板引擎(一)

假设我们要生成下面这样的 html 字符串:

12345678 <div>    <p>welcome, Tom</p>    <ul>        <li>age: 20</li>        <li>weight: 100</li>        <li>height: 170</li>    </ul></div>



要求姓名以及 中的内容是根据变量动态生成的,也就是这样的:

123456 <div>    <p>welcome, {name}</p>    <ul>        {info}    </ul></div>



没接触过模板的同学可能会想到使用字符串格式化的方式来实现:

123456789101112131415161718 HTML = \’\’\’<div>    <p>welcome, {name}</p>    <ul>        {info}    </ul></div>\’\’\’  def gen_html(person):    name = person[\’name\’]    info_list = [        \'<li>{0}: {1}</li>\’.format(item, value)        for item, value in person[\’info\’].items()    ]    info = \’\\n\’.join(info_list)    return HTML.format(name=name, info=info)



这种方案有一个很明显的问题那就是,需要拼接两个 html 片段。 使用过模板技术的同学应该很容易就想到,在 Web 开发中生成 HTML 的更常用的办法是使用模板:

1234567891011121314 HTML = \’\’\’<div>    <p>welcome, {{ person[\’name\’] }}</p>    <ul>        {% for item, value in person[\’info\’].items() %}        <li>{{ item }}: {{ value }}</li>        {% endfor %}    </ul></div>\’\’\’  def gen_html(person):    return Template(HTML).render({\’person\’: person})



本系列文章要讲的就是如何从零开始实现一个这样的模板引擎( Template )。

使用技术

我们将使用将模板编译为 python 代码的方式来解析和渲染模板。 比如上面的模板将被编译为如下 python 代码:

1234567891011121314151617181920212223 def render_function():    result = []     result.extend([        \'<div>\\n\’,        \'<p>welcome, \’        str(person[\’name\’]),        \'</p>\\n\’,        \'<ul>\\n\’    ])    for item, value in person[\’info\’].items():        result.extend([            \'<li>\’,            str(item),            \’: \’,            str(value),            \'</li>\\n\’        ])    result.extend([        \'</ul>\\n\’        ayon-syntax crayon-theme-github crayon-font-monaco crayon-os-pc print-yes notranslate\” data-settings=\” minimize scroll-always\” style=\” margin-top: 12px; margin-bottom: 12px; font-size: 13px !important; line-height: 15px !important;\”>

12345678 <div>    <p>welcome, Tom</p>    <ul>        <li>age: 20</li>        <li>weight: 100</li>        <li>height: 170</li>    </ul></div>




要求姓名以及 中的内容是根据变量动态生成的,也就是这样的:

123456 <div>    <p>welcome, {name}</p>    <ul>        {info}    </ul></div>



没接触过模板的同学可能会想到使用字符串格式化的方式来实现:

123456789101112131415161718 HTML = \’\’\’<div>    <p>welcome, {name}</p>    <ul>        {info}    </ul></div>\’\’\’  def gen_html(person):    name = person[\’name\’]    info_list = [        \'<li>{0}: {1}</li>\’.format(item, value)        for item, value in person[\’info\’].items()    ]    info = \’\\n\’.join(info_list)    return HTML.format(name=name, info=info)



这种方案有一个很明显的问题那就是,需要拼接两个 html 片段。 使用过模板技术的同学应该很容易就想到,在 Web 开发中生成 HTML 的更常用的办法是使用模板:

1234567891011121314 HTML = \’\’\’<div>    <p>welcome, {{ person[\’name\’] }}</p>    <ul>        {% for item, value in person[\’info\’].items() %}        <li>{{ item }}: {{ value }}</li>        {% endfor %}    </ul></div>\’\’\’  def gen_html(person):    return Template(HTML).render({\’person\’: person})



本系列文章要讲的就是如何从零开始实现一个这样的模板引擎( Template )。

使用技术

我们将使用将模板编译为 python 代码的方式来解析和渲染模板。 比如上面的模板将被编译为如下 python 代码:

1234567891011121314151617181920212223 def render_function():    result = []     result.extend([        \'<div>\\n\’,        \'<p>welcome, \’        str(person[\’name\’]),        \'</p>\\n\’,        \'<ul>\\n\’    ])    for item, value in person[\’info\’].items():        result.extend([            \'<li>\’,            str(item),            \’: \’,            str(value),            \'</li>\\n\’        ])    result.extend([        \'</ul>\\n\’        an class=\”crayon-o\”>=[[v[1],v[0]] for v in items]