上篇内容我们详细介绍了将Python中的字符串首字母改为大写的三种方式(罗马不是一天建成的,要日积月累,Python字符串处理你掌握了没?)。其中,我们提到了在string模块有一个创建模板的方法。今天,我们详细了解一下如何使用string模块创建一个自定义的模板类。
简单的模板可以使程序更可读、更高效
理解模板定义的时机
我们在程序中经常会遇到这样的需求:
有一个字典,记录了某学生的信息,如下:
stuInfo = {'ID': '000120', 'name': 'Lucy', 'age': 18, 'home_addr': 'BeiJing', 'final_score': 98}
程序要求我们将这个信息输出成这样:
【学号】000120
【姓名】Lucy
【年龄】18
【地址】BeiJing
【成绩】98
以学生信息格式化输出为例
看过前面章节内容的小伙伴肯定知道,使用format()和'%'都可以实现这样的需求。但是这两种方式都是通过函数(方法)实现的。
如何更精准
假设我们上面格式化的字符串,要在程序中的多个位置使用。此时,我们不可能每次用到上面这串格式化的内容时,都在程序中添加一大串的格式化输出代码。这样,不仅增加了程序的代码量,且会无形中增加了程序的维护难度,有没有代码简化方案?再者,上面的ID、name、age、home_addr、final_score属性对应我们格式化字符串中的学号、姓名、年龄、地址、成绩,每次输出改变的是这些属性的值,如果有一个数据结构(类似)能够像工厂中流水线生产产品一样,只要我们送入原材料(值),它就像机器一样按照指定数量一个个将产品给我们输出出来,这样岂不是更好?假设我们程序的某个地方只需要变量中有下划线的属性:如'home_addr'和'final_score',但是,我们已经定义好了这种输出结构。现在该如何处理呢(我们将这种输出结构定义为一个类,那么只需要修改类属性即可解决这个问题)?此时,定义一个模板就是最佳方案(对于什么是模板,这里就不在粘贴它的定义了)。
想知道的自己搜索吧
如何实现上面的需求
上面也讲了,我们可以使用三种方式来实现它
探讨实现方法
format函数实现
s = "【学号】: {ID}\n【姓名】: {name}\n【年龄】:{age}\n【地址】:{home_addr}\n【成绩】:{final_score}"
print("Format:", s.format(**stuInfo))
'%'实现
s = '''【学号】: %s\n【姓名】: %s\n【年龄】:%s\n【地址】:%s\n【成绩】:%f"
print(s % (stuInfo['ID'], stuInfo['name'], stuInfo['age'], stuInfo['home_addr'], stuInfo['final_score']))
string.Template实现
t = string.Template("【学号】: $ID\n【姓名】: $name\n【年龄】:$age\n【地址】:$home_addr\n【成绩】:$final_score")
print(t.substitute(stuInfo))
从上面很容易看出
对于'%'它具有格式化输出的作用,但是在对其传值时,必须提供变量的类型,这也是其容易出错的原因,很多时候,我们并不需要依次判断变量的类型;
format()函数相对灵活,它不仅提供了格式化输出功能,还能实现模板的一些功能,而且它传入的数据也没有验证数据类型;
Template模板虽然没有进行类型验证,但是它使用面向对象的方式很好的解决了这一问题。
Template模板使用
如何使用Template模板呢?
如何操作呢?
# 导入模块
from string import Template
# 创建模板(此时就生成了一个模板对象,类似工厂里面的机器,但是这个机器还没有给它提供原材料,它还生产不出产品来)
t = Template("【学号】: $ID\n【姓名】: $name\n【年龄】:$age\n【地址】:$Addr\n【成绩】:$Score")
# 生成内容(传入数据)
r = t.substitute(stuInfo)
# 输出结果
>>>print(r)
【学号】: 000120
【姓名】: Lucy
【年龄】:18
【地址】:BeiJing
【成绩】:98
Template类在这里没有进行格式化输出,但是它比较'%'更具有灵活性。
此时,假设程序员在字典中没有将'Score'的内容传入到模板中,程序就会抛出KeyError异常。如果我们不想它抛出异常,可以使用safe_substitute(stuInfo),它会输出这样的内容(直接将变量名称输出出来)
【学号】: 000120
【姓名】: Lucy
【年龄】:18
【地址】:BeiJing
【成绩】:$Score
模板的高级特性
我们先来看下Template提供了哪些属性可供我们使用,还是上面的例子
深入探究
template:输出模板内容。
>>>print(t.template)
【学号】: $ID
【姓名】: $name
【年龄】:$age
【地址】:$Addr
【成绩】:$Score
会将模板原样输出,format和'%'可没有这样的功能哦
delimiter:变量标识符,默认使用"$",当然我们也可以修改它
pattern:模板替换规则,它返回一个正则表达式的字符串形式编译出来的pattern实例,其实就是等价于pattern = re.compile(reg)。它是模板的匹配模式,它必须包含4个命名组(捕获转义定界符、变量名称、加括号的变量名、不合法的定界符模式),具体是哪些东西,下文会涉及。对于re模块不熟悉的小伙伴抓紧时间学习哦!先看一下pattern默认的样子
re.compile('
\$(?:
(?P\$) | # 两个分隔符的转义序列
(?P(?a:[_a-z][_a-z0-9]*)) | # 分隔符和Python标识符,查找变量名的模式
{(?P(?a:[_a-z][_a-z0-9, re.IGNORECASE|re.VERBOSE)
整个模板都是通过这个re模块完成内容匹配的,比如我们上面定义的模板变量名前面加了"$"符,就是通过这个正则表达式来匹配的,甚至于我们匹配的变量名称默认使用这个正则表达式:(?P(?a:[_a-z][_a-z0-9]*))
idpattern:查找变量名的匹配模式
>>>print(t.idpattern)
(?a:[_a-z][_a-z0-9]*)
常用的属性都在这里了。Template模板的强大之处就是通过这些属性来体现的。
如何自定义一个模板
使用下面的方法自定义一个模板类,改变模板匹配规则。
如何自定义一个模板类?
import string
class MyTemplate(string.Template): # 继承模板类
delimiter = "%"
idpattern = '[a-z0-9]*'
这样我们就自定义好了一个模板对象,模板中变量名以"%"开始,并且,变量名称如果是英文字符和0-9数字的都是可以匹配到的。
解决上面的问题
我们在讨论其应用时机时,提出了一个需求,就是加下划线的home_addr和final_score两个属性不需要匹配输出,那么,我们该如何解决呢?
解决问题
import string
class MyTemplate(string.Template): # 继承模板类
idpattern = '[a-z]+_[a-z]+'
可以通过自定义一个模板类的方式对模板中变量进行进一步筛选。
t = MyTemplate('''
【学号】: %ID
【姓名】: %name
【年龄】:%age
【地址】:%home_addr
【成绩】:%final_score
''')
>>>print(t.safe_substitute(stuInfo))
【学号】: %ID
【姓名】: %name
【年龄】:%age
【地址】:BeiJing
【成绩】:98
成功匹配到了变量名中带有下划线的变量(为了区别将前面三个属性也加上去了,具体应用时可直接去除)。
好了,今天的内容就到这里了,我们通过一个案例,详解了Python中Template模板对象的使用时机及方式。对于模板中re模块的匹配不是很理解的小伙伴们抓紧时间学习re模块哦。当然,我们后续内容也会涉及re模块的使用问题,对Python感兴趣的小伙伴们关注我,后续会推出更加精彩的内容。大家也可以在下方留下宝贵的意见建议。我们一起学习、进步……
转载请注明出处,百家号:Python高手养成