Python90-4 使用f-字符串替代C风格字符串和str.format

第4条:使用F字符串替代C风格字符串和str.format

Item 4: Prefer Interpolated F-String Over C-style Format Strings and str.format

字符串贯穿Python的始终。可以用来在用户界面呈现信息和命令行工具。可以用来写入数据到文件和sockets。可以用来描述异常。用来debug。 格式化(Formatting) 字符串是将预先定义的文本数据值结合成可读的信息,存储在字符串中。Python有4种格式化字符串方法(C风格字符串,模板,str.format和f-字符串。也可以将模板方法当成是C风格字符串的改进)。

这些方法中推荐f-字符串,简单易用,可以看成是对前面几种方法的改进。如果对其它方法不感兴趣,可以直接跳到4. f-字符串

1. C风格字符串

(在以前)最常见的是通过%进行格式化(类似于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)

重复容易导致错误,当你做一些修改的时候,很容易忘记某处,导致不一致。

2. 字典格式化字符串(模板)

为了解决其中的一些问题,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,可读性变差。需要有更好的方式来格式化字符串。

3. 内置的str.format

# 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。

4. f-字符串

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_string

F字符串还允许将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字符串的强大特性使其成为使用格式化字符串时的首选。

Things to Remember

  • • C-style格式化字符串使用%,有一些缺陷和冗余。

  • • str.format引入很多有用的概念,但也重复了C-style字符串的问题。

  • • F-strings 是一种新的格式化字符串方法,解决了C字符串的最大问题。

  • • F-strings 简洁强大,允许直接内嵌Python表达式。


原文始发于微信公众号(一只大鸽子):Python90-4 使用f-字符串替代C风格字符串和str.format

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/237455.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!