范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Python大神教你300行代码搞定HTML模板渲染附源码

  一、前言
  模板语言由HTML代码和逻辑控制代码组成,此处   @PHP   。通过模板语言可以快速的生成预想的HTML页面。应该算是后端渲染不可缺少的组成部分。  二、功能介绍
  通过使用学习   tornado   、   bottle   的模板语言,我也效仿着实现可以独立使用的模板渲染的代码模块,模板语法来自   tornado   和   bottle   的语法。可以用来做一些简单的事情   网页渲染   ,   邮件内容生成   等HTML显示方面。以下就是简单的语法使用介绍。
  1. 变量。使用   {{ }}   包裹起来,里面的变量为Python传入。模板渲染时会将传入的变量转换成字符串并填入对应位置。  # 模板文件内容 {{my_title}}  # py代码调用  t_html 为上面的内容 Template(t_html).render(my_title="标题", session = some_obj)
  2. 转义。默认传入的数据都会进行HTML转义,可以使用   {% raw value %}   来将value的内容按原始字符串输出。  # 模板文件内容 

{% raw value %} # Py调用内容 Template(t_html).render(my_title="")   3. 条件控制。支持Python的 if,elif,else 。条件代码需要放在 {% %} 内部,并且在条件结束后需要额外增加 {% end %} ,用于标识条件控制语句块范围。 # 模板文件内容 {% if a > 1%} {% else %} {% end %} # py调用 Template(t_html).render(a=1)   4. 循环控制。支持Python的 for 和 while 。与条件控制一样也需要放在 {% %} 内部,并且结束处需要额外增加 {% end %} ,用于标识循环控制语句块的范围。 # 模板文件内容 {% for i in range(10) %} {% end %} # py调用 Template(t_html).render()   5. 变量声明。如果需要在模板文件内声明一个变量,方便使用时,可以通过 set 来实现。具体格式为 {% set v = xx %} 。通过 set 声明的变量在整个模板文件中都可以使用,包括在条件控制和循环控制中作为条件判断也可以。 # 模板文件内容 {% set a = 1 %} 三、源码   这个模板语言模块是在 Python2.7 上面开发使用的,如果要在 Python3+ 上使用需要对 str 和 bytes 进行一些处理即可,由于没有引用任何其他模块,可以很好地独立使用。 1 # -*- coding:utf-8 -*- 2 3 """ 模板语言""" 4 5 # TOKEN相关的定义 6 TOKEN_S_BRACE = "{" 7 TOKEN_S_BLOCK = "%" 8 TOKEN_EXPRESSION_L = "{{" 9 TOKEN_EXPRESSION_R = "}}" 10 TOKEN_BLOCK_L = "{%" 11 TOKEN_BLOCK_R = "%}" 12 TOKEN_KEY_SET = "set" 13 TOKEN_KEY_RAW = "raw" 14 TOKEN_KEY_IF = "if" 15 TOKEN_KEY_ELIF = "elif" 16 TOKEN_KEY_ELSE = "else" 17 TOKEN_KEY_FOR = "for" 18 TOKEN_KEY_WHILE = "while" 19 TOKEN_KEY_END = "end" 20 TOKEN_KEY_BREAK = "break" 21 TOKEN_KEY_CONTINUE = "continue" 22 TOKEN_SPACE = " " 23 TOKEN_COLON = ":" 24 # Token标记 {{}} {% %} 25 TOKEN_FLAG_SET = {TOKEN_S_BRACE, TOKEN_S_BLOCK} 26 # 简单的语句 27 TOKEN_KEY_SET_SIMPLE_EXPRESSION = {TOKEN_KEY_SET, TOKEN_KEY_RAW} 28 # 前置条件 29 TOKEN_KEY_PRE_CONDITION = { 30 # end 必须在if/elif/else/for/while 后面 31 TOKEN_KEY_END: {TOKEN_KEY_IF, TOKEN_KEY_ELIF, TOKEN_KEY_ELSE, 32 TOKEN_KEY_FOR, TOKEN_KEY_WHILE}, 33 # elif 必须在if 后面 34 TOKEN_KEY_ELIF: {TOKEN_KEY_IF}, 35 # else 必须在if/elif 后面 36 TOKEN_KEY_ELSE: {TOKEN_KEY_IF, TOKEN_KEY_ELIF, TOKEN_KEY_FOR, TOKEN_KEY_WHILE}, 37 } 38 # 循环语句 39 TOKEN_KEY_LOOP = {TOKEN_KEY_WHILE, TOKEN_KEY_FOR} 40 # 循环的控制break continue 41 TOKEN_KEY_LOOP_CTRL = {TOKEN_KEY_BREAK, TOKEN_KEY_CONTINUE} 42 43 class ParseException(Exception): 44 pass 45 46 class TemplateCode(object): 47 def __init__(self): 48 self.codeTrees = {"parent": None, "nodes": []} 49 self.cursor = self.codeTrees 50 self.compiled_code = None 51 52 def create_code(self): 53 """创建一个代码子块""" 54 child_codes = {"parent": self.cursor, "nodes": []} 55 self.cursor["nodes"].append(child_codes) 56 self.cursor = child_codes 57 58 def close_code(self): 59 """ 关闭一个代码子块 """ 60 assert self.cursor["parent"] is not None, "overflow" 61 self.cursor = self.cursor["parent"] 62 63 def append_text(self, text): 64 """ 添加文本 """ 65 # 排除空行 66 self.cursor["nodes"].append("_add(%r)" % text) 67 68 def append_express(self, express, raw=False): 69 """ 表达式 """ 70 if raw: 71 temp_exp = "_t_exp = _str_(%s)" % express 72 else: 73 temp_exp = "_t_exp = _esc_(%s)" % express 74 self.cursor["nodes"].append(temp_exp) 75 self.cursor["nodes"].append("_add(_t_exp)") 76 77 def append_statement(self, statement): 78 """ 语句 """ 79 temp_statement = "%s" % statement 80 self.cursor["nodes"].append(temp_statement) 81 82 def reset(self): 83 self.codeTrees = {"parent": None, "nodes": []} 84 self.cursor = self.codeTrees 85 self.compiled_code = None 86 87 def build_code(self, filename): 88 temp_code_buff = [] 89 self.write_buff_with_indent(temp_code_buff, "def _template_render():", 0) 90 self.write_buff_with_indent(temp_code_buff, "_codes = []", 4) 91 self.write_buff_with_indent(temp_code_buff, "_add = _codes.append", 4) 92 self.write_codes(temp_code_buff, self.codeTrees, 4) 93 self.write_buff_with_indent(temp_code_buff, "return "".join(_codes)", 4) 94 temp_code = "".join(temp_code_buff) 95 self.compiled_code = compile(temp_code,filename, "exec", dont_inherit=True) 96 97 def write_codes(self, code_buff, codes, indent): 98 for node in codes.get("nodes", []): 99 if isinstance(node, dict): 100 self.write_codes(code_buff, node, indent+4) 101 else: 102 self.write_buff_with_indent(code_buff, node, indent) 103 104 def generate(self, **kwargs): 105 temp_namespace = {} 106 temp_namespace["_str_"] = self.to_utf8 107 temp_namespace["_esc_"] = self.to_safe_utf8 108 temp_namespace.update(kwargs) 109 exec(self.compiled_code, temp_namespace) 110 return temp_namespace["_template_render"]() 111 112 @staticmethod 113 def write_buff_with_indent(code_buff, raw_str, indent): 114 """""" 115 temp = (" " * indent) + raw_str + " " 116 code_buff.append(temp) 117 118 @staticmethod 119 def to_utf8(raw_str): 120 """ 转换 """ 121 if isinstance(raw_str, str): 122 return raw_str 123 elif isinstance(raw_str, bytes): 124 return raw_str.decode() 125 return str(raw_str) 126 127 @staticmethod 128 def to_safe_utf8(raw_str): 129 """ 过滤html转义 """ 130 text = TemplateCode.to_utf8(raw_str) 131 return text.replace("&", "&").replace("<", "<").replace(">", ">") 132 class Template(object): 133 """模板类""" 134 def __init__(self, input_obj,filename="", **namespace): 135 """模板初始化""" 136 self.namespace = {} 137 self.namespace.update(namespace) 138 # 将数据丢进去解析生成编译代码 139 self.lexer = TemplateLexer(input_obj, filename) 140 141 def render(self, **kwargs): 142 """渲染模板 """ 143 temp_name_space = {} 144 temp_name_space.update(self.namespace) 145 temp_name_space.update(kwargs) 146 # 执行渲染 147 return self.lexer.render(**kwargs) 148 149 class TemplateLexer(object): 150 """模板语法分析器 """ 151 def __init__(self, input_obb, filename=""): 152 if hasattr(input_obb, "read"): 153 self.raw_string = input_obb.read() 154 else: 155 self.raw_string = input_obb 156 self.filename = filename 157 # 记录当前的位置 158 self.pos = 0 159 # 记录原始数据的总长度 160 self.raw_str_len = len(self.raw_string) 161 # 记录解析的数据 162 self.code_data = TemplateCode() 163 # 开始解析 164 self.parse_template() 165 166 def match(self, keyword, pos=None): 167 return self.raw_string.find(keyword, pos if pos is not None else self.pos) 168 169 def cut(self, size=-1): 170 """剪取数据 size切割数据的大小,-1表示全部""" 171 if size == -1: 172 new_pos = self.raw_str_len 173 else: 174 new_pos = self.pos + size 175 s = self.raw_string[self.pos: new_pos] 176 self.pos = new_pos 177 return s 178 179 def remaining(self): 180 """获取剩余大小 """ 181 return self.raw_str_len - self.pos 182 183 def function_brace(self): 184 """ 获取{{ / {% """ 185 skip_index = self.pos 186 while True: 187 index = self.match(TOKEN_S_BRACE, skip_index) # {% {{ 188 # 没找到 189 if index == -1: 190 return None, -1 191 # 末尾 192 if index >= self.raw_str_len: 193 return None, -1 194 # 匹配类型 195 next_value = self.raw_string[index + 1:index + 2] 196 if next_value not in TOKEN_FLAG_SET: 197 skip_index = index + 1 198 # 说明不是关键类型 199 continue 200 brace = self.raw_string[index: index + 2] 201 return brace, index 202 return None, -1 203 204 def read_content_with_token(self, index, begin_token, end_token): 205 """ 206 读取匹配token的内容 207 """ 208 end_index = self.match(end_token) 209 if end_index == -1: 210 return ParseException("{0} missing end token {1}".format(begin_token, end_token)) 211 # 过滤 begin_token 212 self.pos = index + len(begin_token) 213 content = self.cut(end_index - self.pos) 214 # 去除末尾 end_token 215 self.cut(len(end_token)) 216 return content 217 218 def add_simple_block_statement(self, operator, suffix): 219 if not suffix: 220 raise ParseException("{0} missing content".format(operator)) 221 if operator == TOKEN_KEY_SET: 222 self.code_data.append_statement(suffix) 223 elif operator == TOKEN_KEY_RAW: 224 self.code_data.append_express(suffix, True) 225 else: 226 raise ParseException("{0} is undefined".format(operator)) 227 228 def parse_template(self): 229 """解析模板 """ 230 # TODO 检查模板文件是否更改过,如果没有则不需要重新解析 231 self.code_data.reset() 232 # 解析模板原文件 233 self.__parse() 234 # 生成编译code 235 self.__compiled_code() 236 237 def render(self, **kwargs): 238 return self.code_data.generate(**kwargs) 239 240 def __parse(self, control_operator=None, in_loop=False): 241 """开始解析""" 242 while True: 243 if self.remaining() <= 0: 244 if control_operator or in_loop: 245 raise ParseException("%s missing {%% end %%}" % control_operator) 246 break 247 # 读取 {{ {% 248 brace, index = self.function_brace() 249 # 说明没有找到 250 if not brace: 251 text = self.cut(index) 252 self.code_data.append_text(text) 253 continue 254 else: 255 text = self.cut(index - self.pos) 256 if text: 257 self.code_data.append_text(text) 258 259 if brace == TOKEN_EXPRESSION_L: 260 content = self.read_content_with_token(index, TOKEN_EXPRESSION_L, TOKEN_EXPRESSION_R).strip() 261 if not content: 262 raise ParseException("Empty Express") 263 self.code_data.append_express(content) 264 continue 265 elif brace == TOKEN_BLOCK_L: 266 content = self.read_content_with_token(index, TOKEN_BLOCK_L, TOKEN_BLOCK_R).strip() 267 if not content: 268 raise ParseException("Empty block") 269 270 # 得到表达式 for x in x ; if x ; elif x ; else ; end ; set ; while x ; 271 operator, _, suffix = content.partition(TOKEN_SPACE) 272 if not operator: 273 raise ParseException("block missing operator") 274 275 suffix = suffix.strip() 276 # 简单语句,set / raw 277 if operator in TOKEN_KEY_SET_SIMPLE_EXPRESSION: 278 self.add_simple_block_statement(operator, suffix) 279 elif operator in TOKEN_KEY_LOOP_CTRL: 280 if not in_loop: 281 raise ParseException("{0} must in loop block".format(operator)) 282 self.code_data.append_statement(operator) 283 else: 284 # 控制语句 检查匹配if 后面可以跟elif/else 285 pre_condition = TOKEN_KEY_PRE_CONDITION.get(operator, None) 286 if pre_condition: 287 # 里面就是elif/else/end 288 if control_operator not in pre_condition: 289 raise ParseException("{0} must behind with {1}".format(operator, pre_condition)) 290 elif operator == TOKEN_KEY_END: 291 # 遇到{% end %}则结束 292 self.code_data.close_code() 293 return 294 else: 295 # 由于是依据if 进入 来计算elif ,因此elif与if是同级的 296 self.code_data.close_code() 297 self.code_data.append_statement(content + TOKEN_COLON) 298 self.code_data.create_code() 299 self.__parse(operator, in_loop or (operator in TOKEN_KEY_LOOP)) 300 break 301 # 添加控制语句及内部语句体 if for while 302 self.code_data.append_statement(content + TOKEN_COLON) 303 self.code_data.create_code() 304 self.__parse(operator, in_loop or (operator in TOKEN_KEY_LOOP)) 305 else: 306 raise ParseException("Unkown brace") 307 return 308 309 def __compiled_code(self): 310 """生成 编译code """ 311 self.code_data.build_code(self.filename) 312 if __name__ == "__main__": 313 t = Template("{{hello}}") 314 t.render(hello="你好"   原文链接:   http://www.cnblogs.com/jeffxun/p/15585073.html


行程卡怎么放到桌面上?微信支付宝行程卡均可添加到桌面为了防控疫情,现在出门去很多地方,需要查看行程卡才可进入。这时,我们就要打开微信或者支付宝,然后点击多次找到行程卡,通过手机验证后,就可以看到行程卡了。问题是,要是去的地方多了,每特斯拉卖一辆车赚3。7万,蔚来卖一辆亏2。7万,理想卖一辆亏4。5万众所周知,前段时间特斯拉发布了2季度的报表,从报表来看,特斯拉这一季度是非常给力的,主要指标均在上升,除了比特币亏钱之外,其它都在向好。比如营收高达119。58亿美元(774。5亿防骗8月14日,这60个互联网项目有风险,还是远离吧1很多人咨询为什么不少骗子平台网站在香港?据媒体报道在香港的网站可以不需要有公司营业执照进行验证,也不需要备案。因此只要是互联网项目的网站在香港,你就要警惕了。2任何公司的经营款,截至本月25日,跨境电商独立站单个项目可申请200万补贴此次亚马逊封号潮,得到深圳市商务局高度重视!同时,为鼓励跨境电商企业通过独立站开展业务,深圳市商务局发布利好补贴政策,单个项目择优资助200万!这几天,这个利好补贴政策在各个地方刷两分钟搞懂,五花八门的门禁卡(ID卡IC卡CPU卡),免费复制头条教育星师计划我是IT悟道,点击右上方关注,每天分享IT科技数码方面的干货。五色令人目盲,五音令人耳聋,五味令人口爽,驰骋畋猎令人心发狂,难得之货令人行妨。道德经五花八门的门禁卡中年人耳朵听不清可以配助听器吗?巴南区有没有专业验配的啊?以考虑佩戴助听器这种情况,建议应该去耳鼻喉科检查一下,然后根据检查结果对症治疗,佩戴助听器需要去检查,听力要根据患者的情况选择适合自己的助听器,或者确诊是否能佩戴助听器。不能自己盲轻量办公学习,三千元档雷神一格E1上手分享嘿喽,我是moresure目前主流轻薄本屏幕大部分都是14英寸或者13。3英寸的,尺寸越小越方便携带,不过屏幕尺寸小的同时,近距离办公眼睛的压力还是会比较大,精简布局的键盘始终感觉华为首款科技教育产品华为小精灵学习智慧屏近年来,随着科技生产的进步,以科技来改变学习场景也是越来越普遍,许多家庭都也慢慢的尝试用这种科技教育的新型教育方式,就这样伴随着市场需求的增长,也有越来越多的厂商开始关注这个产品领华为FreeBuds4蜜语红七夕开售在即,让爱的表达更具仪式感莫文蔚的阴天,周杰伦的晴天,都不如和你聊天,明天又到了说情话的日子。七夕是个浪漫的节日,总少不了一些仪式感,而最简单的仪式感便是一份包含浓情爱意的礼物,如果你还在为选择礼物而苦恼,联想要干什么?杨元庆突然宣布,准备自研芯片?从灯塔国开始找华为的麻烦开始,联想的幺蛾子也是不断冒出来。杨元庆甚是成了科技无国界创始人。任正非说华为是一家中国企业,而杨元庆却说联想是一家根植于中国的世界企业,但是联想的水平,却麒麟9010研发成功,明年华为mate50见?前段时间华为P50系列发布,但只支持4G网络这一情况也是被网友给冲了最强5G技术还只支持4G,只支持4G就算了,还卖那么贵卖那么贵就算了,还限量要抢购?一时间也是舆论不断。不过像海
攀升电脑搭载RX6600XT的主机正式上线!想要体验新品显卡的抓紧了昨日,号称要树立1080p高帧率高保真PC游戏新标准的AMDRadeonRX6600XT显卡已正式开卖,而攀升电脑也在第一时间上线了多款搭载有RX6600XT显卡的主机。RX660英伟达游戏显卡和专业显卡的区别我来说一下游戏显卡和专业图形显卡有什么不同。首先两种显卡型号在命名上的区别,游戏显卡我们拿英伟达举例,字母代号为GTGTXRTX而专业图形显卡英伟达系列,无论什么型号,字母代号,都55W功耗32MHs算力RX6600XT显卡或难逃矿工毒手由Reddit网友Trollatopoulous分享的一张显卡功耗算力图表可知AMD新近发布的RadeonRX6600XT显卡,具有相当优秀的每瓦特挖矿算力。这意味着,该卡或已被加三四线城市遍布机器人,中国智造机器人大量上岗曾经,机器人在科幻电影里叱咤风云。现如今,随着科技的快速发展国家政策的推动以及AI相关技术的不断突破,机器人也走出荧屏,进入人们的生活。种类繁多的机器人已经成为人们生产生活的好助手雷军格局超马云,拒绝任何套路,小米不玩锦鲤把戏,怒砸20亿在互联网行业之中,拥有大格局的企业家不多,简直屈指可数。虽然之前笔者一直觉得马云在众多企业大佬之中算是格局比较大的存在了,但现在和雷军一比,差得太远了!可能很多人对于支付宝锦鲤还有腾讯信息处理新专利获批,发明人称为提升信息档案自主可控性近日,腾讯学生身份信息处理专利获授权的新闻登上热搜。据天眼查显示,7月30日,腾讯科技(深圳)有限公司于2019年9月申请的一项名为区块链网络中的学生身份信息处理方法装置电子设备及盘点未来5年最具成长性的5大科技龙头股,AI将掀起新一轮产业革命目前人工智能从概念到实现大规模应用,落地场景十分重要,各知名人工智能企业早已将触角伸向行业各个方面,加速人工智能在工业经济教育医疗航空气象城市治理等领域的落地。我国人工智能市场将持华为手机跌倒,带来的却是整个消费市场换机的低潮从最新数据看,在高端手机市场上,华为跌倒,OV米吃饱,成为了泡影。真论起来,只有苹果一家看起来得利,连三星都不行。而真正发人深省的,是整个手机市场上消费者换手机的意愿陷入低潮。我们吐槽华为鸿蒙的OPPO公关离职并道歉IT之家5月29日消息据中国新闻网报道,5月29日消息,一名OPPO员工因发表吐槽鸿蒙系统的言论离职。5月28日晚,芝士芒胖发微博我没有依据,仅凭一些所谓网络文章和挑动,就否定鸿蒙iPhone13又没有大改动?库克表示你还太年轻多方透露的消息称,今年苹果在9月份会推出全新的iPhone13系列,但是根据以往经验,这次的升级恐怕不会太大,但是外观相较上一代的iPhone可能有较大的变化。最近外国媒体放出了一英媒文章加密货币冬天已经来临英国星期日泰晤士报网站5月23日发表题为要求坏孩子比特币减少碳足迹的压力增强的文章,文章认为,加密货币的冬天已经来临。全文摘编如下在美国比特币基地公司4月成功实现其千亿美元上市前,