内容简介:这两天简单研究了一下web.py框架,发现比较有意思的就是template模板引擎的实现了,所以简单研究了一下基本原理。我们先看一个web.py的模板例子。我有这样一个模板文件:
这两天简单研究了一下web.py框架,发现比较有意思的就是template模板引擎的实现了,所以简单研究了一下基本原理。
例子
我们先看一个web.py的模板例子。
我有这样一个模板文件:
$def with (words) <!DOCTYPE html> <html lang="en"> <head> <title>web-py</title> $:render.header() </head> <body> <h1>$words</h1> <ul> $for i in range(0, 10): <li>$i</li> </ul> </body> </html>
这是一个HTML文件,凡是动态内容,均需要以$开头标识出来,以便模板引擎解析替换。
- $def with (words):这是定义了 python 要传进来的动态参数,在<h1>$words</h1>中渲染。
- $:render.header():这是调用了render对象的header()方法,作用是渲染另外一个模板文件。
- $for i in range(0,10):通过循环制作一个列表。
按照简单理解,python只需要把模板文件读进内存,然后用正则去替换一下$words这样的东西就好了。但是复杂就是,模板引擎还允许执行python代码,比如调用header()函数,for循环,这种用正则怎么可能处理的了呢?所以,模板引擎的实现思路就特别重要了。
模板 -> 代码
如何让模板引擎可以执行Python代码呢?我们不如先看看web.py是如何渲染的。
web.py对上述模板文件经过一番处理,得到的实际上就是一段python代码:
# coding: utf-8 def __template__ (words): __lineoffset__ = -4 loop = ForLoop() self = TemplateResult(); extend_ = self.extend extend_(['\n']) extend_(['<!DOCTYPE html>\n']) extend_(['<html lang="en">\n']) extend_(['<head>\n']) extend_([' <title>web-py</title>\n']) extend_([' ', escape_(render.header(), False), '\n']) extend_(['</head>\n']) extend_(['<body>\n']) extend_([' <h1>', escape_(words, True), '</h1>\n']) extend_([' <ul>\n']) for i in loop.setup(range(0, 10)): extend_([' ', '<li>', escape_(i, True), '</li>\n']) extend_([' </ul>\n']) extend_(['</body>\n']) extend_(['</html>\n']) return self
观察可以发现,这个函数就是HTML模板文件的python实现:
- 模板的入参变成了__template__函数的入参
- 文本的HTML代码变成了python不断extend_追加字符串的过程
- 嵌套渲染其他模板变成了header()函数调用,结果又extend_到最终结果中去
- 循环也变成了真的python代码循环
实际上,模板引擎要做的就是对模板文件进行语法解析,把普通文本转换为extend_(xxx),把python表达式原样的挪过来,最后拼成一个可执行的python函数。
当然,这个解析过程涉及到语法解析的知识,我个人不擅长,所以不展开研究了。
编译代码
当前我们通过语法解析,生成了上述的一段python代码(是一段文本而已),这段代码做的唯一的事情就是定义了一个__template__函数。
现在我们要用这个函数来渲染模板得到最终的HTML,所以需要执行这段代码,得到真正的python可执行函数。
我把这个事情简化一下,现在我们要做的事情是先编译这段代码:
# -*- coding: utf-8 -*- # 纯文本代码 code = """ def f(x): return x * x """ # 编译文本代码, 生成AST抽象语法树 ast = compile(code, '', 'exec')
code中的文本代码,被编译成了AST抽象语法表达树,但是当前还没有被执行。
执行代码
如果code中的代码放在一个.py文件中,我们知道直接调用f()就可以了,但是现在code只是一段字符串,这时候如何执行代码呢?
这时候我们需要用到exec方法,它可以动态执行一段代码,同时允许单独指定代码执行的上下文环境:
# 执行代码 global_env = {} exec(ast, global_env)
exec第一个参数是代码,第二个参数是所处的全局环境,第三个代码是所处的局部环境。
我们知道,当在全局作用域定义函数的时候,函数会被保存到globals()中;在局部作用域(比如某个函数内)定义函数的时候,函数会被保存到locals()中。
所以我们定义一个空字典global_env当做代码执行的全局环境,那么f函数就会保存到global_env中。
调用函数
现在,我们可以从global_env中取出f函数,直接调用它即可:
# 函数f就被定义到global_env中了, 我们可以取出来可执行函数 f = global_env['f'] # 调用它 result = f(5) print(result)
这就是模板引擎的基本工作原理了,所以模板引擎一般都会把编译好的__template__函数cache起来复用,因为如果每次都去解析模板文件再生成可执行函数的消耗有点大。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 网站模板 | 现代时尚创新创意投资组合HTML5模板设计
- ReportLibrary 报表模板库新增 21 张报表模板,加入报表导出功能!
- ReportLibrary 报表模板库新增 21 张报表模板,加入报表导出功能!
- 工具集核心教程 | 第五篇: 利用Velocity模板引擎生成模板代码
- Word 模板引擎 poi-tl V1.3.0 发布,新增模板语法
- React与Vue模板使用比较(一、vue模板与React JSX比较)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
UNIX 时间戳转换
UNIX 时间戳转换
RGB HSV 转换
RGB HSV 互转工具