python template

模板引擎

所谓模板,特别是web模板,是一种表示数据的方式,这种方式旨在提高可读性。模板通常看起来是最终的输出,其中使用占位符代替实际数据(或简化形式的示例数据),带有通用的样式和视觉元素。使用模板呈现的数据可以分为两个部分:template模板本身所需要的数据(如web页面中固定的部分如导航栏、按钮等)、需要呈现(render)的数据data

Python的模板引擎很多,如django template systemJinja 2MakoCheetah3mustache等。更多可参看Templating in Python

这里先介绍下python string模块提供的Template类template stringsmustache,其他可关注后续文章

template strings

  1. $$为转义符号,它会被替换为单个的 $
  2. $identifier 为替换占位符,它会匹配一个名为 "identifier" 的映射键。 在默认情况下,"identifier" 限制为任意 ASCII 字母数字(包括下划线)组成的字符串,不区分大小写,以下划线或 ASCII 字母开头。 在 $ 字符之后的第一个非标识符字符将表明占位符的终结
  3. ${identifier} 等价于 $identifier。 当占位符之后紧跟着有效的但又不是占位符一部分的标识符字符时需要使用,例如 ${noun}ification
  • substitute:执行模板替换,返回一个新字符串
  • safe_substitute:类似于 substitute(),不同之处是如果有占位符未在 mapping 和 kwds 中找到,不是引发 KeyError 异常,而是将原始占位符不加修改地显示在结果字符串中,且任何在其他情况下出现的 $ 将简单地返回 $ 而不是引发 ValueError
  • 使用示例
from string import Template

s = Template('$who likes $what')
print(s.substitute(who='jerry', what='tom'))
d = dict(who='lilei')
# print(s.substitute(d)) # KeyError: 'what'
print(s.safe_substitute(d))

# s = Template('Give $who $100') # ValueError: Invalid placeholder in string: line 1, col 11
s = Template('Give $who $$100')
print(s.substitute(d))

s = Template('${who} likes ${language}books')
print(s.safe_substitute({"who": "jerry", "language": "python"}))
-----------------输出结果------------
jerry likes tom
lilei likes $what
Give lilei $100
jerry likes pythonbooks

简单应用

使用模板template.html生成报告report.html

  1. template.html,其中包含模板占位符${elements}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Great Books of All Time</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <h1>Great Books of All Time</h1>
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">#</th>
                    <th scope="col">Author</th>
                    <th scope="col">Book Title</th>
                </tr>
            </thead>
            <tbody>
                ${elements}
            </tbody>
        </table>
    </div>
</body>
</html>
  1. 作者和书名对应关系data.json
{
    "Dale Carnegie": "How To Win Friends And Influence People",
    "Daniel Kahneman": "Thinking, Fast and Slow",
    "Leo Tolstoy": "Anna Karenina",
    "William Shakespeare": "Hamlet",
    "Franz Kafka": "The Trial"
}
  1. 生成report.html
import json
import string

with open("data.json") as f:
    data = json.loads(f.read())

print(data)

content = ""
for i, (author, title) in enumerate(data.items()):
    content += "<tr>"
    content += f"<td>{i + 1}</td>"
    content += f"<td>{author}</td>"
    content += f"<td>{title}</td>"
    content += "</tr>"

print(content)

with open("template.html") as t:
    template = string.Template(t.read())

final_output = template.substitute(elements=content)
with open("report.html", "w") as output:
    output.write(final_output)

mustache

logic-less templates based on CTemplate ,mustache有很多语言的实现,其中python版本的实现是chevron

安装

 pip install chevron

使用示例

  • 命令行
usage: chevron [-h] [-v] [-d DATA] [-p PARTIALS_PATH] [-e PARTIALS_EXT]
               [-l DEF_LDEL] [-r DEF_RDEL]
               template

positional arguments:
  template              The mustache file

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -d DATA, --data DATA  The json data file
  -p PARTIALS_PATH, --path PARTIALS_PATH
                        The directory where your partials reside
  -e PARTIALS_EXT, --ext PARTIALS_EXT
                        The extension for your mustache partials, 'mustache'
                        by default
  -l DEF_LDEL, --left-delimiter DEF_LDEL
                        The default left delimiter, "{{" by default.
  -r DEF_RDEL, --right-delimiter DEF_RDEL
                        The default right delimiter, "}}" by default.
  • render
def render(template='', data={}, partials_path='.', partials_ext='mustache',
           partials_dict={}, padding='', def_ldel='{{', def_rdel='}}',
           scopes=None, warn=False):
    """Render a mustache template.

    Renders a mustache template with a data scope and partial capability.
    Given the file structure...
    ╷
    ├─╼ main.py
    ├─╼ main.ms
    └─┮ partials
      └── part.ms

    then main.py would make the following call:

    render(open('main.ms', 'r'), {...}, 'partials', 'ms')


    Arguments:

    template      -- A file-like object or a string containing the template

    data          -- A python dictionary with your data scope

    partials_path -- The path to where your partials are stored
                     If set to None, then partials won't be loaded from the file system
                     (defaults to '.')

    partials_ext  -- The extension that you want the parser to look for
                     (defaults to 'mustache')

    partials_dict -- A python dictionary which will be search for partials
                     before the filesystem is. {'include': 'foo'} is the same
                     as a file called include.mustache
                     (defaults to {})

    padding       -- This is for padding partials, and shouldn't be used
                     (but can be if you really want to)

    def_ldel      -- The default left delimiter
                     ("{{" by default, as in spec compliant mustache)

    def_rdel      -- The default right delimiter
                     ("}}" by default, as in spec compliant mustache)

    scopes        -- The list of scopes that get_key will look through

    warn -- Issue a warning to stderr when a template substitution isn't found in the data


    Returns:

    A string containing the rendered template.
    """
...
...
  • 使用示例
import chevron

template = 'Hello, {{ mustache }}!'
data = {'mustache': 'World'}
s = chevron.render(template, data)
print(s)

with open('template.html', 'r') as f:
    s = chevron.render(f, {'mustache': 'World'})
print(s)

args = {
    'template': 'Hello, {{ mustache }}!',

    'data': {
        'mustache': 'World'
    }
}
s = chevron.render(**args)
print(s)


args = {
    'template': 'Hello, {{> thing }}!',

    'partials_dict': {
        'thing': 'World'
    }
}
s = chevron.render(**args)
print(s)

这里用的模板规则都是{{}},更多规则可参考mustache manual

简单应用

还是上面生成报告,同样可以使用 mustache 来实现

  1. 将template.html的模板规则改成{{{ elements }}}
  2. 修改程序如下
import chevron
import json

with open('data.json', 'r') as f:
    data = json.loads(f.read())

content = ""
for i, (author, title) in enumerate(data.items()):
    content += "<tr>"
    content += f"<td>{i + 1}</td>"
    content += f"<td>{author}</td>"
    content += f"<td>{title}</td>"
    content += "</tr>"

output = chevron.render(open('template.html', 'r'), {'elements': content})

with open('report.html', 'w') as f:
    f.write(output)

扩展阅读