Python格式化字符串
Python格式化字符串的方式
方法一(%)
% 格式化方法从 Python 刚开始的时候就存在了,堪称「一届元老」,但是 Python 官方文档中并不推荐这种格式化方式:这里描述的格式化操作容易表现出各种问题导致许多常见错误(例如无法正确显示元组和字典)。使用较新的格式化字符串文字或str.format()可以有助于避免这些错误这些替代方案还提供了更强大,灵活和可打展的格式化文本方法。
- 格式占位符
%s 字符串 (采用str()的显示) %r 字符串 (采用repr()的显示) %c 单个字符 %b 二进制整数 %d 十进制整数 %i 十进制整数 %o 八进制整数 %x 十六进制整数 %e 指数 (基底写为e) %E 指数 (基底写为E) %f 浮点数 %F 浮点数,与上相同 %g 指数(e)或浮点数 (根据显示长度) %G 指数(E)或浮点数 (根据显示长度) %% 字符"%
可以用如下的方式,对格式进行进一步的控制:%[(name)][flags][width].[precision]typecode
-
- (name)为命名
- flags可以有+,-,' '或0。+表示右对齐。-表示左对齐。' '为一个空格,表示在正数的左侧填充一个空格,从而与负数对齐。0表示使用0填充。
- width表示显示宽度
- precision表示小数点后精度
In [24]: print("%+10x" % 10) +a In [25]: print("%04d" % 5) 0005 In [26]: print("%6.3f" % 2.3) 2.300 In [28]: print("%-10x" % 10) a
- 常用转义字符
- 整数的输出
%o —— oct 八进制
%d —— dec 十进制
%x —— hex 十六进制
In [1]: print('%o'%10) 12 In [2]: print('%x'%10) a In [3]: print('%x'%20) 14
- 浮点数输出
%f ——保留小数点后面六位有效数字
%.3f,保留3位小数位
%e ——保留小数点后面六位有效数字,指数形式输出
%.3e,保留3位小数位,使用科学计数法
%g ——在保证六位有效数字的前提下,使用小数方式,否则使用科学计数法
%.3g,保留3位有效数字,使用小数或科学计数法
In [11]: print('%f' % 1.11) # 默认保留6位小数 1.110000 In [12]: print('%.1f' % 1.11) # 取1位小数 1.1 In [13]: print('%e' % 1.11) # 默认6位小数,用科学计数法 1.110000e+00 In [14]: print('%.3e' % 1.11) # 取3位小数,用科学计数法 1.110e+00 In [15]: print('%g' % 1111.1111) # 默认6位有效数字 1111.11 In [16]: print('%.7g' % 1111.1111) # 取7位有效数字 1111.111 In [17]: print('%.2g' % 1111.1111) # 取2位有效数字,自动转换为科学计数法 1.1e+03
- 字符串输出
%s
%10s——右对齐,占位符10位
%-10s——左对齐,占位符10位
%.2s——截取2位字符串
%10.2s——10位占位符,截取两位字符串
In [18]: print('%s' % 'hello world') # 字符串输出 hello world In [19]: print('%20s' % 'hello world') # 右对齐,取20位,不够则补位 hello world In [20]: print('%-20s' % 'hello world') # 左对齐,取20位,不够则补位 hello world In [21]: print('%.2s' % 'hello world') # 取2位 he In [22]: print('%10.2s' % 'hello world') # 右对齐,取2位 he In [23]: print('%-10.2s' % 'hello world') # 左对齐,取2位 he
- 实例
name = '张亚飞' age = 18 job = '学生' hobby = '打篮球' # 格式的字符串与被格式化的字符串必须一一对应,需格式化的字符串多时,容易搞混 print('我叫%s,我的年龄是%d,我是一名%s,我的爱好是%s' % (name, age, job, hobby)) # 通过字典方式格式化,哪个字符将会格式化到哪里,清晰命了 print('我叫%(name)s,我的年龄是%(age)d,我是一名%(job)s,我的爱好是%(hobby)s' % {"name": name, "age": age, "job": job, "hobby":hobby})
方法二 (.format)
方式一:使用位置参数
In [62]: print('My name is {} ,age{}'.format('张亚飞',23)) My name is 张亚飞 ,age23 In [63]: print('My name is {0} ,age{1}'.format('张亚飞',23)) My name is 张亚飞 ,age23
方式二:用字典方式格式化时,指定格式化字符时的位置可以调换
print('hello {name}, you sex is {sex}.'.format(sex='boy', name='tab')) # hello tab, you sex is boy. # '**'起到解包的作用 args = {'name': 'tab', 'sex': 'boy'} print('hello {name}, you sex is {sex}.'.format(**args))
方式三:填充与格式化
[填充字符][对齐方式 <^>][宽度]
In [70]: print('{0:*<10}'.format('张亚飞')) 张亚飞******* In [71]: print('{0:*>10}'.format('张亚飞')) *******张亚飞 In [72]: print('{0:*^10}'.format('张亚飞')) ***张亚飞****
方式四:精度与进制
In [74]: print('{0:.2f}'.format(1232132.12321)) #精确到小数点后两位 1232132.12 In [75]: print('{0:b}'.format(10)) #二进制 1010 In [76]: print('{0:o}'.format(10)) #十进制 12 In [77]: print('{0:x}'.format(10)) #十六进制 a In [78]: print('{0:,}'.format(123232244324)) #千分位格式化 123,232,244,324
方式五:使用索引
In [80]: l1 = ['张亚飞',23] In [85]: l2 = ['hobby','打篮球'] In [86]: print('{0[0]}age is {0[1]},{1[0]}is{1[1]}'.format(l1,l2)) 张亚飞age is 23,hobbyis打篮球
相比%格式化的方式, 推荐使用 .format 方法进行格式化字符串,一来清晰明了,二来效率更高(format 是字符串
str 内置的方法)。更多关于 .format 的用法文档,用户可在交互模式下(终端输入 python
# or ipython or bpython ) 输入 help(str.format) 查看
确实,str.format() 比 %格式化高级了一些,但是它还是有自己的缺陷。
在处理多个参数和更长的字符串时仍然可能非常冗长。
方法三 f-Strings
还好,现在我们有了 f-Strings,它可以使得字符串格式化更加容易。
f-string是以f或F开头的字符串, 其中以{}包含的字符串会进行值替换
下面从多个方面看下 f-strings 的使用方法,看完后,我相信你会对「人生苦短,我用 Python」有更深地赞同~
1 f-Strings 使用方法
name = '张亚飞' age = 18 job = '学生' hobby = '打篮球' print(f'我叫{name},我的年龄是{age},我是一名{job},我的爱好是{hobby}') # 进制转换 print(f"小明今年{22}岁") print(f"小明今年{22:#b}岁") print(f"小明今年{22:#o}岁") print(f"小明今年{22:#x}岁") # 填充与格式化 print(f'{name:*<10}') print(f'{name:*>10}') print(f'{name:*^10}') # python3.8引入 text = f"小明的名字叫喵喵,今年{19 + 2=}岁" print(text)
其他使用方法与fotmat类似
因为 f-strings 是在运行时计算的,那么这就意味着你可以在其中放置任意合法的 Python 表达式,比如:
- 运算表达式
f'{2 ** 3 + 1}' Out[222]: '9'
- 调用函数
还可以调用函数:
def test(name): return name + ' really' f'{test(name)} is handsome' Out[231]: 'ZhangYafei really is handsome'
也可以直接调用函数
f'{name.lower()} is handsome' Out[232]: 'zhangyafei is handsome'
2. 类中使用
class People(object): def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f'{self.name} is {self.age}' def __repr__(self): return f'{self.name} is {self.age}. HAHA!' person = People() ZYF = People('ZhangYafei', 24) F'{ZYF}' Out[236]: 'ZhangYafei is 24' F'{ZYF!r}' Out[237]: 'ZhangYafei is 24. HAHA!' ZYF Out[238]: ZhangYafei is 24. HAHA! print(ZYF) ZhangYafei is 24
3. 多行f-string
name = 'ZhangYafei' age = 24 status = 'Python' message = { f'hi {name}' f'you are {age}' f'you are learning {status}' } message Out[245]: {'hi ZhangYafeiyou are 24you are learning Python'}
这里需要注意,每行都要加上 f 前缀,否则格式化会不起作用:
4 速度对比
其实,f-string 里的 f 也许可以代表 fast,它比 %格式化方法和 str.format() 都要快:
from timeit import timeit def test_baifenhao(): name = 'ZhangYafei' age = 24 return '%s is %s.'%(name, age) def test_format(): name = 'ZhangYafei' age = 24 return '{} is {}.'.format(name, age) def test_f_string(): name = 'ZhangYafei' age = 24 return f'{name} is {age}.' timeit(test_baifenhao, number=1000000) Out[265]: 0.7293048258696899 timeit(test_format, number=1000000) Out[266]: 0.8064396247757486 timeit(test_f_string, number=1000000) Out[267]: 0.5187802432072317
5 注意事项
5.1 引号的处理
可以在字符串中使用各种引号,只要保证和外部的引号不重复即可。
以下使用方式都是没问题的:
f"{'ZhangYafei'}" Out[268]: 'ZhangYafei' f'{"ZhangYafei"}' Out[269]: 'ZhangYafei' f"""ZhangYafei""" Out[270]: 'ZhangYafei' f'''ZhangYafei''' Out[271]: 'ZhangYafei'
那如果字符串内部的引号和外部的引号相同时呢?那就需要 进行转义:
f'you are very \'handsome\'' Out[272]: "you are very 'handsome'"
5.2 括号的处理
若字符串中包含括号 {},那么你就需要用双括号包裹它:
f'{{100}}' Out[273]: '{100}' f'{{{100}}}' Out[275]: '{100}' f'{{{{100}}}}' Out[274]: '{{100}}' f'{{{{{100}}}}}' Out[276]: '{{100}}' f'{{{{{{100}}}}}}' Out[277]: '{{{100}}}'
5.3 反斜杠
上面说了,可以用反斜杠进行转义字符,但是不能在 f-string 表达式中使用:
5.4 注释符号
不能在表达式中出现 #,否则会报出异常;
四、Python标准库模板
来看一个例子
from string import Template t = Template('Hey, $name!') t.substitute(name=name) Out[280]: 'Hey, ZhangYafei!'
你可以看到我们需要先从Python的内建 string 模块中导入 Template 类。模板字符串并不是核心的语言特征,但是它们由Python标准库的string提供的。
再看几个例子
# 引用string模块中的Template类 from string import Template t = Template('我叫$name,我的年龄是$age,我是一名$job,我的爱好是$hobby') print(t.substitute(name=name, age=age, job=job, hobby=hobby)) template1 = Template('$s是我最喜欢的编程语言,$s非常容易学习,而且$s的功能很强大。') # 指定格式化参数s的值是Python print(template1.substitute(s='Python')) # 当格式化参数是一个字符串的一部分时,为了和字符串的其他部分区分开,需要用一对大括号将格式化参数变量括起来 template2 = Template('${s}stitute') print(template2.substitute(s='sub')) template3 = Template('$dollar$$相当于多少$pounds') # 替换两个格式化参数变量 print(template3.substitute(dollar=20, pounds='英镑')) template4 = Template('$dollar$$相当于多少$pounds') data = {} data['dollar'] = 100 data['pounds'] = '英镑' # 使用字典指定格式化参数 print(template4.substitute(data))
其他一些复杂的字符串格式化技巧的可能会给你的程序带来安全漏洞。
In [5]: 'class of {0} is {0.__class__}'.format(42) Out[5]: "class of 42 is <class 'int'>" ...: pass ...: ...: user_input = '{event.__init__.__globals__[SECRET]}' ...: event = Event() ...: print(user_input.format(event=event)) this-is-a-secret In [7]: from string import Template ...: ...: user_input = '${event.__init__.__globals__[SECRET]}' ...: print(Template(user_input).substitute(event=event)) f:\python37\lib\string.py in _invalid(self, mo) 103 lineno = len(lines) 104 raise ValueError('Invalid placeholder in string: line %d, col %d' % --> 105 (lineno, colno)) 106 107 def substitute(*args, **kws): ValueError: Invalid placeholder in string: line 1, col 1
Python字符串格式化经验法则
Python字符串格式化经验法则:如果你的格式化字符串是由用户提供的,那么就是用模板字符串(#4)避免安全问题。不然如果是Python 3.6+的话,就使用字符串插值/f-Strings,如果不是就使用“新式”字符串格式化(str.format)。
总结重点
● 也许感到惊讶,Python在处理字符串格式化时不只有一种方法。
● 每个方法都有其优缺点。你应该根据你的用例来选择合适的方法