在Python中格式化字符串最常用的方法是使用%操作符。预定义的文本模板在格式字符串中运算符的左侧。要插入的值作为单个值或多个值的元组提供在格式运算符的右侧。
a = 0b10111011
b = 0xc5f
print('Binary is %d, hex is %d' % (a, b))
>>> Binary is 187, hex is 316
格式字符串使用格式说明符(如%d)作为占位符,将被格式表达式右侧的值替换。格式说明符的语法来自C的printf函数,这是Python继承的。Python支持您所期望的所有常规选项printf,例如%s、%x和%f格式说明符,以及对小数点、padding、fill和对齐。
Python中的C格式字符串有四个问题。
第一个问题是,如果更改格式化表达式右侧元组中数据值的类型或顺序,可能会由于类型转换不兼容而出错。
key = 'my_var'
value = 1.234
formatted = '%-10s = %.2f' % (key, value)
print(formatted)
>>> my_var = 1.23
如果交互key和value,将会得到一个运行时异常
reordered_tuple = '%-10s = %.2f' % (value, key)
>>> Traceback ...
TypeError: must be real number, not str
reordered_string = '%.2f = %-10s' % (key, value)
>>> Traceback ...
TypeError: must be real number, not str
第二个问题是在值格式化为字符串之前,你需要对其进行小的修改时,就会变得难以阅读。这是一个很常见的需求。
pantry = [ ('avocados', 1.25), ('bananas', 2.5), ('cherries', 15), ]
for i, (item, count) in enumerate(pantry):
print('#%d: %-10s = %.2f' % (i, item, count))
>>>
#0: avocados = 1.25
#1: bananas = 2.50
#2: cherries = 15.00
现在,我对正在格式化的值进行了一些修改使打印的信息更有用。这会导致元组在格式表达式太长,需要拆分 跨多行,这会影响可读性:
for i, (item, count) in enumerate(pantry):
print('#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count)))
>>>
#1: Avocados = 1
#2: Bananas = 2
#3: Cherries = 15
格式化表达式的第三个问题是要在格式字符串中多次使用相同的值,必须在右边的元组中重复它
template = '%s loves food. See %s cook.'
name = 'Max'
formatted = template % (name, name)
print(formatted)
>>>
Max loves food. See Max cook.
为了帮助解决其中一些问题,Python中的%运算符还可以使用字典进行格式化。
解决问题1
key = 'my_var'
value = 1.234
old_way = '%-10s = %.2f' % (key, value)
new_way = '%(key)-10s = %(value).2f' % { 'key': key, 'value': value} # Original
reordered = '%(key)-10s = %(value).2f' % { 'value': value, 'key': key} # Swapped
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
然而,字典格式字符串引入并加剧了其他问题。对于上面的问题#2,关于值的小修改在格式化它们之前,格式化表达式会变得越来越长由于存在字典键和右边的冒号操作符。
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:在格式化表达式时使用字典也会增加冗长 每个键必须至少指定两次,一次在格式规范中指定,一次在字典中指定为键,另一次可能在包含字典值的变量名
soup = 'lentil'
formatted = 'Today\'s soup is %(soup)s.' % {'soup': soup} print(formatted)
>>>
Today's soup is lentil.
The format Built-in and str.format
Python3增加了对高级字符串格式的支持,比使用%运算符的旧C样式格式字符串更具表现力。 对于单个Python值,可以访问这个新功能,通过内置的格式函数
a = 1234.5678
formatted = format(a, ',.2f')
print(formatted)
b = 'my string'
formatted = format(b, '^20s')
print('*', formatted, '*')
>>>
1,234.57
* my string *
可以使用此功能将多个值一起格式化,通过调用str类型的新格式方法
key = 'my_var'
value = 1.234
formatted = '{} = {}'.format(key, value)
print(formatted)
>>>
my_var = 1.234
formatted = '{:<10} = {:.2f}'.format(key, value)
print(formatted)
>>>
my_var = 1.23
在大括号中,还可以指定一个位置索引传递给格式方法以用于替换占位符的参数
formatted = '{1} = {0}'.format(key, value)
print(formatted)
>>>
1.234 = my_var
formatted = '{0} loves food. See {0} cook.'.format(name)
print(formatted)
>>>
Max loves food. See Max cook.
并未解决问题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
插值格式字符串 f-string
key = 'my_var'
value = 1.234
formatted = f'{key} = {value}'
print(formatted)
>>>
my_var = 1.234
formatted = f'{key!r:<10} = {value:.2f}'
print(formatted)
>>>
'my_var' = 1.23
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_string
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