用 Python 编写一个模板引擎(上)

简介: 用 Python 编写一个模板引擎

一直对模板引擎的实现很好奇,正好看到了这篇文章,翻译一下,供大家学习、参考。原文和 GitHub 链接在文后。

我们编写一个最简单的模板引擎,并且探索一下它的底层实现。如果你想直接看代码的话,GitHub 是你的好朋友

语言设计

这里设计的模板语言非常基础。使用两种标签,变量和块。

<!-- 变量使用 `{{` 和 `}}` 作为标识-->
<div>{{my_var}}</div>
<!-- 块使用 `{%` 和 `%}` 作为标识-->
{% each items %}
    <div>{{it}}</div>
{% end %}

大多数的块需要使用关闭标签,关闭标签使用 {%end%}表示。

这个模板引擎能够处理基本的循环和条件语句,而且也支持在块中使用 callable。在我看来,能够在模板中调用任意的 Python 函数非常方便。

循环

使用循环可以遍历集合或者 iterable。

{% each people %}
   <div>{{it.name}}</div>
{% end %}
{% each [1, 2, 3] %}
   <div>{{it}}</div>
{% end %}
{% each records %}
    <div>{{..name}}</div>
{% end %}

在上面的例子里面,people 是一个集合, it 指向了当前迭代的元素。使用点分隔的路径会被解析成字典属性。使用 .. 可以访问外部上下文中的对象。

条件语句

条件语句不需要多解释。这个语言支持 if 和 else 结构,而且支持 ==, <=, >=, !=, is, <, > 这几个操作符。

{% ifnum 5 %}
    <div>more than 5</div>
{% else %}
    <div>less than or equal to 5</div>
{% end %}

调用块

Callable 可以通过模板上下文传递,并且使用普通位置参数或者具名参数调用。调用块不需要使用 end 关闭。

<!-- 使用普通参数... -->
<div class='date'>{% call prettify date_created %}</div>
<!-- ...使用具名参数 -->
<div>{% call log 'here' verbosity='debug' %}</div>

原理

在探索引擎是如何编译和渲染模板之前,我们需要了解下在内存中如何表示一个编译好的模板。

编译器使用抽象语法树(Abstract Syntax Tree, AST)来表示计算机程序。AST 是对源代码进行词法分析(lexical analysis)的结果。AST 相对源代码来说有很多好处,比如说它不包含任何无关紧要的文本元素,比如说分隔符这种。而且,树中的节点可以使用属性来添加更多的功能,而不需要改动代码。

我们会解析并分析模板来构造这样一棵树,并用它来表示编译后的模板。渲染的时候,遍历这棵树,传给它对应的上下文,然后输出 HTML。

模板切词(tokenize)

解析的第一步是把内容分隔成不同的片段。每个片段可以是任意的 HTML 或者是一个标签。这里使用正则表达式和 split() 函数分隔文本。

VAR_TOKEN_START = '{{'
VAR_TOKEN_END = '}}'
BLOCK_TOKEN_START = '{%'
BLOCK_TOKEN_END = '%}'
TOK_REGEX = re.compile(r"(%s.*?%s|%s.*?%s)" %(
    VAR_TOKEN_START,
    VAR_TOKEN_END,
    BLOCK_TOKEN_START,
    BLOCK_TOKEN_END))

让我们来看一下 TOKREGEX。可以看到这个正则的意思是 TOKREGEX 要么是一个变量标签,要么是一个块标签,这是为了让变量标签和块标签都能够分隔文本。表达式的最外层是一个括号,用来捕获匹配到的文本。其中的 ? 表示非贪婪的匹配。我们想让我们的正则表达式是惰性的,并且在第一次匹配到的时候停下来。

下面这个例子实际展示了一下上面的正则:

>>> TOK_REGEX.split('{% each vars %}<i>{{it}}</i>{% endeach %}') ['{% each vars %}',<i>''{{it}}', '</i>, '{% endeach %}']

把每个片段封装成 Fragment 对象。这个对象包含了片段的类型,并且可以作为编译函数的参数。片段有以下四种类型

VAR_FRAGMENT = 0
OPEN_BLOCK_FRAGMENT = 1
CLOSE_BLOCK_FRAGMENT = 2
TEXT_FRAGMENT = 3


目录
相关文章
|
自然语言处理 安全 编译器
用 Python 编写一个模板引擎(下)
用 Python 编写一个模板引擎
82 0
|
2月前
|
前端开发 开发者 Python
从零到一:Python Web框架中的模板引擎入门与进阶
在Web开发的广阔世界里,模板引擎是连接后端逻辑与前端展示的重要桥梁。对于Python Web开发者而言,掌握模板引擎的使用是从零到一构建动态网站或应用不可或缺的一步。本文将带你从基础入门到进阶应用,深入了解Python Web框架中的模板引擎。
31 3
|
4月前
|
前端开发 数据库 开发者
构建可维护的Web应用:Python模板引擎与ORM的协同工作
【7月更文挑战第19天】在Web开发中,可维护性至关重要。Python搭配Flask或Django框架,利用模板引擎(如Jinja2)和ORM(如SQLAlchemy或Django ORM)增强开发效率和代码质量。模板引擎桥接前后端,ORM简化数据库操作,两者协同提升可读性和可测试性。例如,Flask用Jinja2渲染动态HTML,Django通过ORM处理数据库模型。这种分离关注点的方法降低了耦合,增强了应用的可维护性。
45 1
|
4月前
|
前端开发 JavaScript 数据处理
深入Python Web开发:模板引擎的力量与最佳实践
【7月更文挑战第21天】Python Web开发中,模板引擎如Jinja2促进MVC架构的View层,分离后端数据与前端展示,提升开发效率和代码复用。选择适合的模板引擎,利用其数据注入、模板继承等特性,保持模板简洁,注重安全性,是最佳实践。例如,Jinja2允许在HTML中嵌入变量并处理循环,简化渲染过程。
48 0
|
4月前
|
前端开发 开发者 Python
从零到一:Python Web框架中的模板引擎入门与进阶
【7月更文挑战第20天】模板引擎如Jinja2在Python Web开发中连接后端与前端,提高代码可读性和协作效率。Flask默认集成Jinja2,提供条件语句、循环、宏和模板继承等功能。例如,创建一个简单Flask应用,渲染&quot;Hello, World!&quot;,并展示如何使用条件语句和循环处理数据。通过宏定义重用代码,模板继承实现页面结构共享。学习模板引擎能提升开发效率和项目质量。
58 0
|
4月前
|
SQL 前端开发 数据库
Python Web开发进阶之路:从模板引擎到ORM的全面掌握
【7月更文挑战第19天】在Python Web开发中,提升技能的关键是理解和运用模板引擎(如Jinja2)与ORM技术。模板引擎,如在Flask中使用的Jinja2,使前端HTML与后端逻辑分离,便于维护。例如,通过路由函数`show_posts()`和`render_template()`呈现文章列表模板,用Jinja2的`for`循环展示内容。ORM,如SQLAlchemy,提供Pythonic的数据库操作,将表映射为类,SQL转化为方法调用。在博客系统中,定义Post模型并与数据库交互,展示了ORM简化数据管理的优势。通过实践这些工具,开发者能更好地驾驭复杂的Web项目。
54 0
|
6月前
|
XML 缓存 API
Python 模板引擎 Jinja2 的安装和使用
Python 模板引擎 Jinja2 的安装和使用
191 0
|
XML 数据格式 Python
Python Flask 编程 | 连载 05 - Jinja2 模板引擎
Python Flask 编程 | 连载 05 - Jinja2 模板引擎
Python Flask 编程 | 连载 05 - Jinja2 模板引擎
|
Python
Python:正则re.sub实现简易的模板引擎
Python:正则re.sub实现简易的模板引擎
110 0
带你读《Python Flask Web开发入门与项目实战》之三:Jinja 2模板引擎
本书从Flask框架的基础知识讲起,逐步深入到使用Flask进行Web应用开发实战。内容通俗易懂,案例丰富,实用性强,特别适合Python Web开发的入门读者和进阶读者学习,也适合PHP程序员和Java程序员等其他Web开发爱好者阅读。另外,本书可以作为相关培训机构的教材用书。