Jinj2 模版
一、Jinja2 基础知识
1.1 Jinja2 是什么?
Jinja2 一个基于 Python 开发的通用模板语言,它具有 灵活、快速 和 安全 等特性
1.2 Jinja2 模版是做什么呢?
以 ansible 为例,基于 jinja2 模版我们可以实现更丰富更灵活的文件配置
例如:我们希望让 redis 服务监听在本地非回环地址上,如下所示:
bind 192.168.10.120
可每个节点地址都不同,我们该如何做呢?
答案是 ansible facts
+ jinja2 template
# ansible_host 为清单上的 IP
bind {{ ansible_host }}
基于此,我们就可以实现基于各服务器自适应的服务配置,如下所示:
配置文件模版 redis.conf.j2
bind {{ redis_listen_host }}
protected-mode yes
port {{ redis_listen_port }}
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize {{ set_daemon | default('yes') }}
supervised no
pidfile /var/run/redis_{{ redis_listen_host }}.pid
loglevel notice
logfile /var/log/redis/redis_{{ redis_listen_host }}.log
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
剧本定义
---
- hosts: ecs
gather_facts: yes
vars:
redis_listen_host: "{{ ansible_eth0.ipv4.address }}"
redis_listen_port: 6380
tasks:
- name: "基于 Facts 生成 Redis 配置"
template:
src: templates/redis.conf.j2
dest: /tmp/redis.conf
执行命令
$ ansible-playbook jinja2-demo-0.yml
PLAY [ecs] ******
TASK [Gathering Facts] ******
ok: [ecs-1.aliyun.sz]
ok: [huawei]
TASK [基于 Facts 生成 Redis 配置] ******
changed: [ecs-1.aliyun.sz]
ok: [huawei]
PLAY RECAP ******
ecs-1.aliyun.sz : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
huawei : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
查看文件
$ ansible 'ecs' -m shell -a "head -n5 /tmp/redis.conf"
ecs-1.aliyun.sz | CHANGED | rc=0 >>
bind 172.25.163.48
protected-mode yes
port 6380
tcp-backlog 511
timeout 0
huawei | CHANGED | rc=0 >>
bind 192.168.0.101
protected-mode yes
port 6380
tcp-backlog 511
timeout 0
OK,可以看到,我们基于 facts + jinja2 实现个性化的 redis 配置文件。 jinja2 算是一项比较通用的技术,不仅用在 Ansible,Python 中的 Django、Flask 等框架都有广泛使用,所以系统性的掌握它性价比还是很高的~
二、Jinja2 模版语法
Ansible 使用 jinja2 模版引擎,我们先来了解下 jinja2 的语法知识
早先我们在 playbook 是这么引用变量 {{ variable }}
的,其实这里的 {{ }}
就是 jinja 的核心语法之一,除了它以外还有另外两个 {% %}
、``,我们分别来看下
{{ }}
:用来装载 表达式,如变量、运算表达式、比较表达式等{% %}
:用来装载 控制语句,如 if 控制结构,for 循环控制结构- `` :用来装载 注释,模板文件被渲染后,注释不会包含在最终生成的文件中
基本上 jinja2 的一切特性都是从这三组符号展开的,我们不着急,一个一个来看…
2.1 表达式
{{ }}
表达式
大致内容
2.1.1 变量
先看第一个例子:
模版定义
Test jinja2 variables
my name is {{ username }}.
执行效果,渲染模版时手动传值,供以模版使用,效果等同于 vars
关键字
$ ansible 'ecs[0]' -m template -e "username=Da" -a "src=templates/expression_variables-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "893d60bda9b0810037d8732483ad1de78045823a",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "6ba43e7ab4675870ee0334ade7bdd6d3",
"mode": "0644",
"owner": "root",
"size": 36,
"src": "/root/.ansible/tmp/ansible-tmp-1634111293.09-20738-180061502458356/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
Test jinja2 variables
my name is Da.
在看个稍微复杂点的
剧本定义
---
- hosts: ecs
gather_facts: no
vars:
str_var: "Da"
num_var: 18
list_var: ["Python", "Go"]
dict_var:
name: "{{ str_var }}"
age: "{{ num_var }}"
skill: "{{ list_var }}"
tasks:
- name: "变量传递 至 jinja2 模版"
template:
src: templates/expression_variables-demo-2.j2
dest: /tmp/test.j2
模版定义
Test jinja2 variables
字符串变量:{{ str_var }}
整型变量:{{ num_var }}
列表变量:{{ list_var }}
字典变量:{{ dict_var }}
执行命令
$ ansible-playbook jinja2-demo-1.yml
PLAY [ecs] ******
TASK [变量传递 至 jinja2 模版] ******
changed: [ecs-1.aliyun.sz]
changed: [huawei]
PLAY RECAP ******
ecs-1.aliyun.sz : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
huawei : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
查看文件
$ cat /tmp/test.j2
Test jinja2 variables
字符串变量:Da
整型变量:18
列表变量:[u'Python', u'Go']
字典变量:{u'skill': [u'Python', u'Go'], u'age': 18, u'name': u'Da'}
2.1.2 比较运算
模版定义
Jinja2 比较表达式
{{ 1 == 1 }} {# 返回 True #}
{{ 2 != 1 }} {# 返回 False #}
{{ 2 > 1 }} {# 返回 True #}
{{ 2 >= 1 }} {# 返回 True #}
{{ 2 < 1 }} {# 返回 False #}
{{ 2 <= 1 }} {# 返回 False #}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/expression_comparison-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "b1c42819c64b16488c03a45e1af71ff4eb1d3293",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "6c08fd4745825983cf496abb9ac46cec",
"mode": "0644",
"owner": "root",
"size": 63,
"src": "/root/.ansible/tmp/ansible-tmp-1634111715.29-21192-555943826307/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
Jinja2 比较表达式
True
True
True
True
False
False
2.1.3 逻辑运算
模版定义
Jinja2 逻辑运算
{{ (2 > 1) or (1 < 2) }} {# 只要有一个括号返回 True 即可 #}
{{ (2 > 1) and (1 > 2) }} {# 两个括号都得返回 True 才行 #}
{{ not true }} {# 非 True == False #}
{{ not True }} {# 同上 #}
{{ not false }} {# 非 False == True #}
{{ not False }} {# 同上 #}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/expression_logical-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "34f1f77306db50e1ec5cdb90849d36f2fae0454e",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "c8ab48b35ec939c923e0fe138eef60bf",
"mode": "0644",
"owner": "root",
"size": 114,
"src": "/root/.ansible/tmp/ansible-tmp-1634112063.22-21529-92332521163295/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
Jinja2 逻辑运算
True
False
False
False
True
True
2.1.4 三元运算
我们可以通过 if
表达式实现三元运算的效果,语法规则与 Python 十分类似,基本语法
<do something> if <something is true> else <do something else>
In[2]: '小于' if 1 < 2 else '大于'
Out[2]: '小于'
模版定义
if 表达式 jinja2 使用
1 {{ '小于' if 1 < 2 else '大于' }} 2
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/expression_if-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "d6c19b4dbb1b9faf318dd1a9d079972b09e057f9",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "87af82bd45d6c3b51eea373f652d381d",
"mode": "0644",
"owner": "root",
"size": 39,
"src": "/root/.ansible/tmp/ansible-tmp-1634126704.98-32325-990148113389/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
if 表达式 jinja2 使用
1 小于 2
2.1.4 算数运算
模版定义
Jinja2 算数运算
{{ 3 + 2 }} {# 求和: 5 #}
{{ 3 - 4 }} {# 减法:-1 #}
{{ 3 * 5 }} {# 乘法:15 #}
{{ 2 ** 3 }} {# 乘方:8 #}
{{ 7 / 5 }} {# 除法:1.4 #}
{{ 7 // 5 }} {# 整除:1 #}
{{ 17 % 5 }} {# 取模:2 #}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/expression_arithmetic-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "622bf544c7c391d3ae07b11fbe7fad439490e7cf",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "f43e889b6abffd882c22a5d41e798225",
"mode": "0644",
"owner": "root",
"size": 57,
"src": "/root/.ansible/tmp/ansible-tmp-1634112450.88-22066-279556856379893/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
Jinja2 算数运算
5
-1
15
8
1.4
1
2
2.1.5 成员运算
模版定义
Jinja2 成员表达式
{{ 1 in [1, 2, 3] }} {# 1 存在于列表中, 返回 True #}
{{ 1 not in (1, 2, 3) }} {# 1 不存在于列表中,返回 False #}
{{ "a" in 'abcd' }} {# 1 不存在于列表中,返回 False #}
{{ "name" in {"name": "Da", "age": 18} }} {# name 字段在于字典中,返回 True #}
{{ "gender" in {"name": "Da", "age": 18} }} {# gender 字段在于字典中,返回 False #}
{{ "Go" in {"name": "Da", "age": 18, "skill": ["Python", "Go"]}.skill }} {# Go 元素是否在 skill 列表中,返回 True #}
{{ "DevOps" in {"name": "Da", "age": 18, "skill": ["Python", "Go"]}['skill'] }} {# DevOps 元素是否在 skill 列表中,返回 False #}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/expression_member-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "07518c5d5c40905280ddec91368a2e3ac6a82583",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "100f5655ba1e892c44f17dd46bc0bc2e",
"mode": "0644",
"owner": "root",
"size": 148,
"src": "/root/.ansible/tmp/ansible-tmp-1634112908.08-22582-277982919935613/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
Jinja2 成员表达式
True
False
True
True
False
True
False
2.1.6 过滤器
过滤器也可以直接在 {{ }}
中使用,用法在 playbook 中一样,先回顾下此前学过的过滤器内容
模版定义
Jinja2 过滤器
my name is {{ da | capitalize }}.
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/expression_filter-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "9824ca3a09b7b55deb3e8ab78f4a8e8389584803",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "c2a7187567ba6a412a3fed4ea2b11fe8",
"mode": "0644",
"owner": "root",
"size": 31,
"src": "/root/.ansible/tmp/ansible-tmp-1634113897.73-23643-75275671536157/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
Jinja2 过滤器
my name is Da.
2.1.7 tests
tests 在 jinja2 自然也是可以的,老规矩先复习下
模版定义
Jinja2 Tests 判断
{{ username is defined }}
{{ password is undefined }}
{{ '/tmp' is exists }}
{{ '/tmp/testfile' is file }}
{{ '/tmp' is directory }}
执行效果
$ ansible 'ecs[0]' -m template -e "username=Da" -a "src=templates/expression_tests-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "9ba832404175d5a702941d0267d0770b462ea1a8",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "805b7a379b9eb25909aec930662d95ae",
"mode": "0644",
"owner": "root",
"size": 51,
"src": "/root/.ansible/tmp/ansible-tmp-1634114252.36-24012-167926043248952/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
Jinja2 Tests 判断
True
True
True
False
True
2.1.8 lookup
lookup 插件的主要功能是拉取数据,例如从剧本中的数据容器、系统文件、服务等等
模版定义
lookup jinja2 使用
文件内容:{{ lookup('file', '/tmp/t1.txt') }}
系统变量 Path:{{ lookup('env','PATH') }}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/expression_lookup-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "e099dca996b690545a6a45a56793ab6fbbe20aff",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "fe0890921d3bb66126a1858aca8c81a3",
"mode": "0644",
"owner": "root",
"size": 184,
"src": "/root/.ansible/tmp/ansible-tmp-1634114867.89-24590-240242574038716/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
lookup jinja2 使用
文件内容:t1 file.
系统变量 Path:/root/.pyenv/shims:/root/.pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/prometheus
2.2 控制语句
{% %}
控制语句
上面对 {{}}
有了一个基本的了解,下面我们看 {% %}
控制语句,它的主要用途包括 变量声明
、条件判断
、循环
、宏
以及模版的 包含
、导入
、继承
等等
大致内容
2.2.0 变量声明
先来看最基本的变量声明,语法格式:{% set variable='value' %}
模版定义
set 控制语句 jinja2 使用
{% set username='Da' %}
My name is {{ username }}.
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/control_set-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "bd7b989112ef6ca626389dcb80d2926da4780207",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "eece8afab519037dcc5a6ed9286d78b7",
"mode": "0644",
"owner": "root",
"size": 47,
"src": "/root/.ansible/tmp/ansible-tmp-1634132882.13-4326-179634076713335/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
set 控制语句 jinja2 使用
My name is Da.
2.2.1 条件判断
条件判断,jinja2 的条件判断是通过 if 条件语句
,基本结构为三种
if 结构
{% if 条件 %}
...
{% endif %}
if else 结构
{% if 条件 %}
...
{% else %}
...
{% endif %}
if elif else 结构
{% if 条件一 %}
...
{% elif 条件二 %}
...
{% elif 条件N %}
...
{% endif %}
模版定义
if 控制语句 jinja2 使用
{% set username='Da' %}
{% set password='123123' %}
{% if username=='Da' and password=='123123' %}
登录成功~
{% elif username=='Da' or password=='123123' %}
用户名或密码错误!
{% else %}
登录失败!
{% endif %}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/control_if-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "03fd800cfeae0e35e3ba4ab5faad2cd0d9b62e26",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "47483a89432ded0f8812d5b4f5d1c0aa",
"mode": "0644",
"owner": "root",
"size": 48,
"src": "/root/.ansible/tmp/ansible-tmp-1634133411.82-5184-5999937262592/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
if 控制语句 jinja2 使用
登录成功~
2.2.2 循环迭代
接下来是循环,这可是重头戏,提起一口气,准备开干吧!
1. 列表遍历
先来个最简单的 for 循环遍历 list 的示例
模版定义
for 控制语句 jinja2 使用
{% for i in [1, 2, 3] %}
元素:{{ i }}
{% endfor %}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/control_for-demo-1.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "e5673a43aec2c6890bb2965fe31ae3bcfd62284e",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "cc20e94475195b122c6079b7c55cf261",
"mode": "0644",
"owner": "root",
"size": 65,
"src": "/root/.ansible/tmp/ansible-tmp-1634133741.51-5480-266927481354358/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
for 控制语句 jinja2 使用
元素:1
元素:2
元素:3
默认元素后面是会附加 “换行” 的效果,如果不希望它换行,可以这么做
模版定义
for
语句的右边结束符 以及 endfor
左边开始符 加上 -
for 控制语句 jinja2 使用
{% for i in [1, 2, 3] -%}
元素:{{ i }}
{%- endfor %}
查看文件
$ cat /tmp/test.j2
for 控制语句 jinja2 使用
元素:1元素:2元素:3
可以看到,所有元素直接接连拼在了一起,我们可以自定义分割符,如下
模版定义
{% for i in [1, 2, 3] -%}
{# 两种方式效果相同 #}
元素:{{ i }}{{ ',' }}
{# 元素:{{ i~',' }} #}
{%- endfor %}
查看文件
$ cat /tmp/test.j2
for 控制语句 jinja2 使用
元素:1,元素:2,元素:3,
在 jinja2 中 波浪符 ~
为字符串连接符,常用于将波浪符 ~
将 迭代变量 或 其他字符串连在一起,你可以理解为 Python 中的 +
,如下所示:
{% for i in [1, 2, 3] -%}
{{ i~',' }}
{%- endfor %}
等于
for i in [1, 2, 3]:
str(i)+','
2. 字典遍历
模版定义
通过 字典的 iteritems()
方法获取 key
与 value
for 控制语句 jinja2 使用
{% for i in {'name': 'Da', 'age': 18}.iteritems() %}
{{ key ~ ':' ~ val }}
{% endfor %}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/control_for-demo-2.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "ad1b88881c2e63d165680df48a84b6439971db26",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "18992c00f35cce54f4d2df0fee74dc74",
"mode": "0644",
"owner": "root",
"size": 47,
"src": "/root/.ansible/tmp/ansible-tmp-1634210315.83-5571-259042812590635/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
for 控制语句 jinja2 使用
age:18
name:Da
基本用法和 Python 没太大区别
for key, value in {'name': 'Da', 'age': 18}.items():
print(f'{key}:{value}')
name:Da
age:18
3. 内置变量
我们可以通过内置变量 loop.index
获取当前是第几次循环,示例如下:
模版定义
for 控制语句 jinja2 使用
{% for i in ['Da','Yo','Ho'] %}
{{ '第' ~ loop.index ~ '次遍历,用户名称:' ~ i }}
{% endfor %}
执行效果
$ ansible 'ecs[0]' -m template -a "src=templates/control_for-demo-3.j2 dest=/tmp/test.j2"
ecs-1.aliyun.sz | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "e635899d0b3b11507a6f732e3f1b7615e892e90e",
"dest": "/tmp/test.j2",
"gid": 0,
"group": "root",
"md5sum": "d6f8c1f9cc38a9a4e1979492f75e1e78",
"mode": "0644",
"owner": "root",
"size": 134,
"src": "/root/.ansible/tmp/ansible-tmp-1634210652.82-6250-154771205134999/source",
"state": "file",
"uid": 0
}
查看文件
$ cat /tmp/test.j2
for 控制语句 jinja2 使用
第1次遍历,用户名称:Da
第2次遍历,用户名称:Yo
第3次遍历,用户名称:Ho
除了 loop.index
外,还有其他内置变量,这里贴下,大致了解下过个眼熟
内置变量 | 描述 |
---|---|
loop.index |
当前循环操作为整个循环的第几次循环,以 1 开始 |
loop.index0 |
当前循环操作为整个循环的第几次循环,以 0 开始 |
loop.revindex |
当前循环操作距离整个循环结束还有几次,到 1 结束 |
loop.revindex0 |
当前循环操作距离整个循环结束还有几次,到 0 结束 |
loop.first |
当操作可迭代对象中的第一个元素时,此变量的值为 true |
loop.last |
当操作可迭代对象中的最后一个元素时,此变量的值为 true |
loop.length |
可迭代对象的长度 |
loop.depth |
当使用递归的循环时,当前迭代所在的递归中的层级,层级序号从 1 开始 |
loop.depth0 |
当使用递归的循环时,当前迭代所在的递归中的层级,层级序号从 0 开始 |
loop.cycle() |
辅助函数,通过这个函数我们可以在指定的一些值中进行轮询取值(todo) |
4. 循环判断
基于已学的内容,我们基本可以写出循环内嵌套判断的 jinja2 代码,例如:打印列表 [1,2,3,4,5] 中大于 3 的元素