
字符串贯穿Python的始终。可以用来在用户界面呈现信息和命令行工具。可以用来写入数据到文件和sockets。可以用来描述异常。用来debug。 格式化(Formatting) 字符串是将预先定义的文本和数据值结合成可读的信息,存储在字符串中。Python有4种格式化字符串方法(C风格字符串,模板,str.format和f-字符串。也可以将模板方法当成是C风格字符串的改进)。
这些方法中推荐f-字符串,简单易用,可以看成是对前面几种方法的改进。如果对其它方法不感兴趣,可以直接跳到4. f-字符串
(在以前)最常见的是通过%进行格式化(类似于C的格式化字符串)。
a = 0b10111011
b = 0xc5f
print('Binary is %d, hex is %d' % (a, b))output:
Binary is 187, hex is 3167这种方式使用格式说明符(如%d)作为占位符(placeholders),占位符将被 % 右侧的实际值替代。这种方式来自C语言的printf函数。Python支持各种常见的说明符,如%s,%x,%f等。Python中C-style的格式化字符串有如下4个问题:第一个问题就是如果你改变data values的类型或顺序时,格式说明符也要相应修改,否则就会出错。(顺序敏感)
key = 'my_var'
value = 1.234
#correct
formatted = '%-10s = %.2f' % (key,value)
print(formatted)
#改变数据顺序,就会出错
formatted = '%-10s = %.2f' % (value,key)
print(formatted)output:
Binary is 187, hex is 3167
my_var = 1.23
Traceback (most recent call last):
File "D:/PY_TEST/python90/4_format_string.py", line 10, in
formatted = '%-10s = %.2f' % (value,key)
TypeError: must be real number, not str第二个问题是C-style格式字符串可读性很差。尤其是当你想做一些修改的时候。
# second problem
pantry = [
('avocados', 1.25),
('bananas', 2.5),
('cherries', 15),
]
for i , (item, count) in enumerate(pantry):
print('#%d: %-10s = %.2f' % (i, item, count))
# make a few modifications to values,become so long
for i , (item, count) in enumerate(pantry):
print('#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count)))第三个问题是如果你想用同一个值多次,你需要在%右侧重复多次。
template = '%s loves food. See %s cook.'
name = 'Max'
formatted = template % (name, name)
print(formatted)重复容易导致错误,当你做一些修改的时候,很容易忘记某处,导致不一致。
为了解决其中的一些问题,Python中%右侧可以使用字典(dictionary)而不是元组(tuple)。这可以解决上面的问题1(顺序敏感)。
# dict
key = 'my_var'
value = 1.234
old_way = '%-10s = %.2f' % (key, value)
new_way = '%(key)-10s = %(value).2f' % {
'key': key, 'value': value}
reordered = '%(key)-10s = %(value).2f' % {
'value': value, 'key': key}
assert old_way == new_way == reordered字典也解决了问题3(重复).
name = 'Max'
template = '%s loves food. See %s cook.'
before = template % (name, name) # Tuple
template = '%(name)s loves food. See %(name)s cook.'
after = template % {'name': name} # Dictionary
assert before == after但是,字典格式化字符串引入了一些其它问题。例如问题二,使用字典会使语句更长。
# make other problems
for i, (item, count) in enumerate(pantry):
before = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
after = '#%(loop)d: %(item)-10s = %(count)d' % {
'loop': i + 1,
'item': item.title(),
'count': round(count),
}
assert before == after使用字典格式化字符串同样增加冗长。也就是问题4。每个key至少出现两次,一次在描述符,一次在字典中,并且还可能作为变量名出现。
# verbosity, soup 出现3次。
soup = 'lentil'
formatted = 'Today\'s soup is %(soup)s.' % {'soup': soup}
print(formatted)menu = {
'soup': 'lentil',
'oyster': 'kumamoto',
'special': 'schnitzel',
}
template = ('Today\'s soup is %(soup)s, '
'buy one get two %(oyster)s oysters, '
'and our special entrée is %(special)s.')
formatted = template % menu
print(formatted)为了理解格式化字符串会产生什么,需要一边看menu,一边看template,可读性变差。需要有更好的方式来格式化字符串。
# format
a = 1234.5678
formatted = format(a, ',.2f')
print(formatted)
b = 'my string'
formatted = format(b,'^20s')
print(formatted)可以简单使用{}作为占位符,不用%d这种。
key = 'my_var'
value = 1.234
formatted = '{} = {}'.format(key, value)
print(formatted)也可以在{}内使用:说明符
key = 'my_var'
value = 1.234
formatted = '{:<10} = {:.2f}'.format(key, value)
print(formatted)格式化操作可以在每个类的__format__方法里修改。在C字符串里,如果想输出%,应该用%%,同样,str.format里输出{}要用{{}}
print('%.2f%%' % 12.5)
print('{} replaces {{}}'.format(1.23)){}内也可以指定索引,这允许字符串更新顺序,而不需要改变右侧的内容。解决了问题1(顺序敏感).
formatted = '{1} = {0}'.format(key, value)也可以解决问题3,重复的问题。
formatted = '{0} loves food. See {0} cook.'.format(name)可惜的是,format方法没有解决问题2。看起来依旧难读。
for i, (item, count) in enumerate(pantry):
old_style = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
new_style = '#{}: {:<10s} = {}'.format(
i + 1,
item.title(),
round(count))
assert old_style == new_style使用format有更好的选项:
formatted = 'First letter is {menu[oyster][0]!r}'.format(
menu=menu)
print(formatted)但是问题4,重复key导致冗余的问题还是存在。
old_template = (
'Today\'s soup is %(soup)s, '
'buy one get two %(oyster)s oysters, '
'and our special entrée is %(special)s.')
old_formatted = template % {
'soup': 'lentil',
'oyster': 'kumamoto',
'special': 'schnitzel',
}
new_template = (
'Today\'s soup is {soup}, '
'buy one get two {oyster} oysters, '
'and our special entrée is {special}.')
new_formatted = new_template.format(
soup='lentil',
oyster='kumamoto',
special='schnitzel',
)
assert old_formatted == new_formatted看起来好了一些,因为减少了字典中的一些引号。但还是很冗长。
鉴于这些问题的存在,总体上不推荐str.format。
Python3.6 添加了插值格式字符串(Interpolated Format Strings),简称为f-stirngs,来解决上述问题。使用时在字符串前加上f,就像byte string加前缀b,原始字符串加前缀r。
key = 'my_var'
value = 1.234
formatted = f'{key} = {value}' # f-字符串
print(formatted)str.format的操作都可以在f字符串中使用。
formatted = f'{key!r:<10} = {value:.2f}'
print(formatted)f字符串比之前的更简洁
f_string = f'{key:<10} = {value:.2f}'
c_tuple = '%-10s = %.2f' % (key, value)
str_args = '{:<10} = {:.2f}'.format(key, value)
str_kw = '{key:<10} = {value:.2f}'.format(key=key,
value=value)
c_dict = '%(key)-10s = %(value).2f' % {'key': key,
'value': value}
assert c_tuple == c_dict == f_string
assert str_args == str_kw == f_stringF字符串还允许将Python表达式放入{}内,解决了问题2(可读性差)。
for i, (item, count) in enumerate(pantry):
old_style = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
new_style = '#{}: {:<10s} = {}'.format(
i + 1,
item.title(),
round(count))
f_string = f'#{i+1}: {item.title():<10s} = {round(count)}'
assert old_style == new_style == f_string你也可以F字符串拆成多行,看起来会更加清晰。
for i, (item, count) in enumerate(pantry):
print(f'#{i+1}: '
f'{item.title():<10s} = '
f'{round(count)}')Python表达式也可以出现在格式说明符中:
places = 3
number = 1.23456
print(f'My number is {number:.{places}f}')F字符串的强大特性使其成为使用格式化字符串时的首选。