好好学Python:从零基础到项目实战
上QQ阅读APP看书,第一时间看更新

4.2 字符串格式化

在实际项目应用中,字符串的格式化操作应用得非常广,特别是在项目的调试及项目日志的打印中,都需要通过字符串的格式化方式来得到更精美的打印结果。本节将介绍Python中字符串格式化的方式。

4.2.1 经典的字符串格式化符号——百分号(%)

当你第一眼看到百分号(%)这个符号时,你可能会想到运算符中的取模操作。在做运算时,这么理解是没有问题的,但在字符串操作中,百分号(%)还有一个更大的用途,就是字符串的格式化。百分号(%)是Python中最经典的字符串格式化符号,也是Python中最古老的字符串格式化符号,这是Python格式化的OG(original generation),伴随着python语言的诞生。

先看看百分号(%)做字符串格式化的一些示例:

>>> print('hi,%s' % 'python')
hi,python
>>> print('一年有%s个月' % 12)
一年有12个月

由上面输出结果可以看到,在做字符串的格式化时,百分号(%)左边和右边分别对应了一个字符串,左边放置的是一个待格式化的字符串,右边放置的是要被格式化的值。

为便于美观,一般书写方式是在%的左边和右边各加一个空格,以便于指明这是在做格式化,当然,不加空格也可以,执行时并不会报错。

被格式化的值可以是一个字符串或数字。

待格式化字符串中的%s部分称为转换说明符,表示该位置需要放置被格式化的对象,通用术语为占位符。可以想象成在学校上自习,有的同学为避免自己的位置被其他同学占据,当离开座位时,通常会放一个物品在这个位置上,其他人过来时,若看到有物品在这个位置上,就知道这个位置已经有人了。这里就可以把%s当作我们使用的物品,作为一个占位的声明,放物品在位置上的同学就相当于百分号(%)右边的值。

在前面列举的示例中,%s也是有具体含义的,%s表示的意思是百分号(%)右边要被格式化的值会被格式化为字符串,%s中的s指的是str,即字符串。如果百分号(%)右边要被格式化的值不是字符串,就会使用字符串中的str()方法将非字符串转换为字符串。如示例中就将整数值12转换为字符串了,这种方式对大多数数值都有效。

若需要将数据转换为其他格式,该用怎样的方式处理?Python为我们提供了多种格式化符号,如表4-2所示。

表4-2 字符串格式化符号

根据表4-2,“一年有12个月”这个示例可以使用以下两种方式来表示:

>>> print('一年有%s个月' % 12)  #使用%s作为12的占位符
一年有12个月
>>> print('一年有%d个月' % 12)  #使用%d作为12的占位符
一年有12个月

由输出结果可以看到,对于整数类型,可以使用%s将整数类型格式化为字符串类型,也可以使用%d将变量直接格式化为整数类型。

上面讲解了整型的格式化,浮点型的格式化是怎样的呢?

在Python中,浮点型的格式化使用字符%f进行,例如:

>>> print('圆周率PI的值为:%f' % 3.14)
圆周率PI的值为:3.140000

由输出结果可以看到,结果中有很多位小数,但指定的被格式化的值只有两位小数。这里怎么让格式化的输出和指定的被格式化的值一致呢?

仔细查看表4-2可知,%f在使用时,可以指定精度值。在Python中,使用%f时,若不指定精度,则默认输出6位小数。若需要以指定的精度输出,比如上面想要得到输出2位小数的结果,就要指定精度为2,指定精度为2的格式如下:

%.2f

指定精度的输出的基本格式:在百分号(%)后面跟上一个英文格式下的句号,接着加上希望输出的小数位数,最后加上浮点型格式化字符f。

例如,上面圆周率输出的示例可以更改为如下的格式化输出:

>>> print('圆周率PI的值为:%.2f' % 3.14)
圆周率PI的值为:3.14

由输出结果可以看到,输出的结果已经符合预期结果了。

对于表4-2中所列举的字符串格式化符号,并不是所有的都比较常用,其中比较常用的只有%s、%d、%f三个,%e和%E在科学计算中使用得比较多,对于其他字符串格式化符号,大概了解即可,有兴趣也可以自行研究。

假如要输出类似1.23%这样格式的结果,通过格式化的方式该怎么处理?直接使用加号连接符连接一个百分号可以吗?在交互模式下尝试如下:

>>> print('今天的空气质量比昨天提升了:%.2f' % 1.23+'%')
今天的空气质量比昨天提升了:1.23%

由输出结果可以看到,使用加号连接符连接百分号的方式可以得到最终结果,但编写的代码看起来怪怪的,也不太美观,有没有更美观的编写方式?在交互模式下尝试如下:

>>> print('今天的空气质量比昨天提升了:%.2f%%' % 1.23)
今天的空气质量比昨天提升了:1.23%

由输出结果可以看到,用上面这种方式也得到了想要的结果。不过从输入代码中可以看到,字符f后面使用了两个百分号(%),打印出来的结果只有一个百分号(%),这是怎么回事?

在Python中,字符串格式转化时,遇到的第一个百分号(%)指的是转换说明符,如果要输出百分号这个字符,就需要使用%%的形式才能得到百分号字符(%)。使用%%这种方式的效果如下:

>>> print('输出百分号:%s' % '%')
输出百分号:%

4.2.2 元组的字符串格式化

格式化操作符的右操作数可以是任何元素,但如果右操作数是元组或映射类型(如字典,下一章进行讲解),那么字符串格式化的方式将会有所不同。目前尚未涉及映射(字典),这里先了解元组的字符串格式化。

如果右操作数是元组,元组中的每个元素都会被单独格式化,每个值都需要对应的一个占位符。例如:

由输出结果可以看到,在有多个占位符的字符串中,可以通过元组传入多个待格式化的值。若字符串中有多个占位符,但给的待格式化的值的格式不对或数量不对,执行报错。若字符串中占位符的个数少于给定元组中元素的个数,执行报错。若使用列表代替元组,列表仅代表一个值。

在前面一些示例的演示后,接下来介绍占位符的一些基本使用说明。注意,占位符中各项的顺序是至关重要的。

(1)%字符:标记占位符开始。

(2)最小字段宽度(可选):转换后的字符串至少应该具有该值指定的宽度。如果是*,宽度就会从元组中读出。

(3)转换标志(可选):-表示对齐;+表示在转换值之前要加上正负号;“”(空白字符)表示正数之前保留空格;0表示转换值位数不够时用0填充。

(4)点(.)后跟精度值(可选):如果转换的是实数,精度值表示出现在小数点后的位数;如果转换的是字符串,该数字就表示最大宽度;如果是*,精度就会从元组中读出。

(5)转换类型:参见表4-2。

下面将分别讨论。

1.简单字符串格式化

在交互模式下输入:

>>> print('圆周率PI的值为:%.2f' % 3.14)
圆周率PI的值为:3.14
>>> print('石油价格为每桶:$%d' % 96)
石油价格为每桶:$96

由输出结果可以看到,简单的字符串格式化只需要在占位符中标识转换类型。

2.格式化时指定字段宽度和精度

占位符包括对字段格式化时字段宽度和精度的指定。字段宽度是转换后的值所保留的最小字符个数,字符精度是数字转换结果中应该包含的小数位数或字符串转换后的值所能包含的最大字符个数。

在交互模式下输入:

由输出结果可知,占位符中的字段宽度和精度值都是整数,宽度和精度之间通过点号(.)分隔。字段宽度和精度两个参数都是可选参数,如果给出精度,在精度值前就必须包含点号。

接着看以下代码:

>>> print('从元组中获取字符串精度:%*.*s' % (10,5,'hello world'))
从元组中获取字符串精度:     hello             #输出字符串宽度为10、精度为5
>>> print('从元组中获取字符串精度:%.*s' % (5,'hello world'))
从元组中获取字符串精度:hello                  #输出精度为5

由输出结果可以看到,可以使用*作为字段宽度或精度(或两者都用*),数值会从元组中读出。

3.符号、对齐和0填充

开始介绍之前先看一个示例:

>>> print('圆周率PI的值为:%010.2f' % 3.141593)
圆周率PI的值为:0000003.14

输出结果是不是怪怪的,这个我们称之为“标表”。在字段宽度和精度之前可以放置一个“标表”,可以是零、加号、减号或空格。零表示用0进行填充。

减号(-)用来左对齐数值,例如:

>>> print('圆周率PI的值为:%10.2f' % 3.14)
圆周率PI的值为:      3.14
>>> print('圆周率PI的值为:%-10.2f' % 3.14)
圆周率PI的值为:3.14      #此处右侧为多出的空格

从输出结果看到,使用减号时,数字右侧多出了额外的空格。

空白(“ ”)表示在正数前加上空格,例如:

>>> print(('% 5d' % 10) + '\n' + ('% 5d' % -10))
   10
  -10

由输出结果可以看到,该操作可以用于对齐正负数。

加号(+)表示无论是正数还是负数都表示出符号,例如:

>>> print(('宽度前加加号:%+5d' % 10) + '\n' + ('宽度前加加号:%+5d' % -10))
宽度前加加号:  +10
宽度前加加号:  -10

该操作也可以用于数值的对齐。

4.2.3 format字符串格式化

从Python 3.6开始,引入了另外一种字符串格式化的方式,形式为str.format()。str.format()是对百分号(%)格式化的改进。使用str.format()时,替换字段部分使用花括号表示。在交互模式下输入:

    >>> 'hello,{}'.format('world')
    'hello,world'
    >>> print('圆周率PI的值为:{0}'.format(3.141593))
    圆周率PI的值为:3.141593
    >>> print('圆周率PI的值为:{0:.2f}'.format(3.141593))
    圆周率PI的值为:3.14
    >>> print('圆周率PI的值为:{pi}'.format(pi=3.141593))
    圆周率PI的值为:3.141593
    >>> print('{}年的冬奥会将在{}举行,预测中国至少赢取{}枚金牌'.format('2022','北京
',5))
    2022年的冬奥会将在北京举行,预测中国至少赢取5枚金牌
    >>> print('{0}年的冬奥会将在{1}举行,预测中国至少赢取{2}枚金牌'.format('2022','北京
',5))
    2022年的冬奥会将在北京举行,预测中国至少赢取5枚金牌
    >>> print('{0}年的冬奥会将在{2}举行,预测中国至少赢取{1}枚金牌'.format ('2022',5,'
北京'))
    2022年的冬奥会将在北京举行,预测中国至少赢取5枚金牌
    >>> print('{year}年的冬奥会将在{address}举行'.format(year='2022',address='北京
'))
    2022年的冬奥会将在北京举行

由输出结果可以看到,str.format()的使用形式为:用一个点号连接字符串和格式化值,多于一个的格式化值需要用元组表示。字符串中,带格式化的占位符用花括号({})表示。

花括号中可以没有任何内容,没有任何内容时,若有多个占位符,则元组中元素的个数需要和占位符的个数一致。

花括号中可以使用数字,数字指的是元组中元素的索引下标,字符串中花括号中的索引下标不能超过元组中最大的索引下标,元组中的元素值可以不全部使用。如以下示例:

>>> print('{0}年的冬奥会将在{2}举行'.format('2022',5,'beijing','sh'))
2022年的冬奥会将在beijing举行

花括号中可以使用变量名,在元组中对变量名赋值。花括号中的所有变量名,在元组中必须要有对应的变量定义并被赋值。元组中定义的变量可以不出现在字符串的花括号中。如下面的示例所示:

    >>> print('{year}年的冬奥会将在{address}举行'.format(year='2022',address='北京',
num=5))
    2022年的冬奥会将在北京举行

4.2.4 f字符串格式化

从Python 3.6开始,引入了一种新的字符串格式化字符:_f-strings_,格式化字符串。

使用f字符串做格式化可以节省很多的时间,使格式化更容易。f字符串格式化也称为“格式化字符串文字”,因为f字符串格式化是开头有一个f的字符串文字,即使用f格式化字符串时,需在字符串前加一个f前缀。

f字符串格式化包含了由花括号括起来的替换字段,替换字段是表达式,它们会在运行时计算,然后使用format()协议进行格式化。

_f-strings_使用方式如下:

>>> f'hello,{world}'
'hello,world'
>>> f'{2*10}'
'20'
>>> year = 2022
>>> address = '北京'
>>> gold = 5
>>> f'{year}年的冬奥会将在{address}举行,预测中国至少赢取{gold}枚金牌'
'2022年的冬奥会将在北京举行,预测中国至少赢取5枚金牌'
>>> print(f'{year}年的冬奥会将在{address}举行,预测中国至少赢取{gold}枚金牌')
2022年的冬奥会将在北京举行,预测中国至少赢取5枚金牌

由输出结果可以看到,使用f做字符串格式化也是非常方便的。

在Python中,使用百分号(%)、str.format()形式可以格式化的字符串,都可以使用f字符串格式化实现。

提示

在后续章节中,会更多地使用str.format()和f的形式做格式化,百分号(%)格式化的方式能不用就不用。

4.2.5 f-string字符串格式化

f-string(或者称为“格式化字符串”)在Python 3.6版本中加入的,虽然这一特性非常方便,但是开发者发现f-string对调试没有帮助。因此,Eric V. Smith为f-string添加了一些语法结构,使其能够用于调试。

在过去,f-string这样使用:

>>> name='xiaomeng'
>>> number=1001
>>> print(f'name={name}, number={number}')
name=xiaomeng, number=1001

在Python 3.8中,可以使用如下方式(更加简洁):

>>> name='xiaomeng'
>>> number=1001
>>> print(f'{name=}, {number=}')
name='xiaomeng', number=1001

f字符串格式可以更方便地在同一个表达式内进行输出文本和值或变量的计算,而且效率更高。

在过去,f-string这样使用:

>>> x=5
>>> print(f'{x+1}')
6

在Python 3.9中,可以输出表达式及计算结果,操作如下:

>>> x=5
>>> print(f'{x+1=}')
x+1=6

对于小数,若需要输出指定位数,可以如下操作:

>>> import math
>>> print(f'{math.pi=}')
math.pi=3.141592653589793
>>> print(f'{math.pi=:.3}') # 输出3位数,小数位两位
math.pi=3.14

由输出可以看到,对于小数的输出,:.3中的3是指输出的总位数,而不是指小数位数,在使用过程中需要注意。