用户:Xyy23330121/Python/格式化字符串
格式化字符串是一种字符串处理的方法。
str.format
方法可以按模式更改字符串中的内容。我们用以下示例来展示该方法的特征:
a = "{} World"
print(a) #输出:{} World
print(a.format("Hello")) #输出:Hello World
print(a) #输出:{} World
可以看出,a.format("Hello")
输出了字符串 a
被替换掉大括号 {}
的版本,没有更改字符串本身。
我们可以按顺序设置以下内容,从而进行更高级的替换。
字符串中,如果不指定任何内容,大括号会被按输入参数的顺序替换。
print("{} {}".format("Hello", "World", "!")) #输出:Hello World
大括号中间如果有索引,则会按输入参数的索引替换内容。
print("{1} {2}".format("Hello", "World", "!")) #输出:World !
print("{0} {0}".format("Hello", "World", "!")) #输出:Hello Hello
大括号中间如果有标识符,则会按照标识符,查找传入的参数名称,来确定替换的内容。
print("{first} {second}".format(first = "Hello", third = "World", second = "!")) #输出:Hello !
print("{third} {third}".format(first = "Hello", third = "World", second = "!")) #输出:World World
在指定了参数后,我们还可以进一步指定参数的元素或属性。
我们可以给输入的内容指定索引。
print("{0[0]} {2}".format("Hello", "World", "!")) #输出:H !
要注意,此时索引仅支持非负整数。如果输入以下的内容,就会报错:
print("{0[0:]} {2}".format("Hello", "World", "!"))
#TypeError: string indices must be integers, not 'str'
在替换时,Python 如果识别到方括号里面有不在 0
~9
中的字符,就会把索引作为字符串传入。也就是说,在本例中,指定的替换内容是"Hello"["0:"]
,而非"Hello"[0:]
。这对于参数是字典的时候很有帮助,但对于参数是字符串、元组或列表的时候则相反。
解决方法见之后的 类 章节。
print("{0.start} {2}".format(range(3), "World", "!")) #输出:0 !
类似索引,我们也可以指定属性。
使用索引或属性的次数是没有限制的。比如:
print("{0[0][1]} {2}".format(["Hello"], "World", "!")) #输出:e !
字符 | 意思 |
---|---|
s |
用str 函数转换
|
r |
用repr 函数转换
|
a |
用ascii 函数转换
|
在确定了用于替换的内容后,我们可以指定转换方式。
print("{0[0]!s} {2}".format(["Hello"], "World", "!")) #输出:Hello !
通过输入 !
,再输入代表转换方式的字符,我们可以决定如何把参数的内容转换为字符串。以上面的0[0]!s
为例,此时,Python 会用 str(0[0])
(即 str(["Hello"][0])
)的内容去替换原字符串的内容。
repr
函数会尝试返回一串字符串,使得程序员按照字符串输入内容,就能直接生成对应的对象。比如:
print(str("114514")) #输出:114514
print(repr("114514")) #输出:'114514'
如果程序员按照 repr
函数返回的字符串,输入了 '114514'
。那么该程序员会得到与 repr
函数参数相同的一个对象。
<generator object <genexpr> at *>
中的*
是程序运行时,该对象被存储于内存中的位置,或者说,指向该对象的指针。在每次运行中,由于存储对象所使用的内存位置不同,该值也是不同的。但这也不绝对,比如:
print(str(i for i in range(3))) #输出:<generator object <genexpr> at *>
print(repr(i for i in range(3))) #输出:<generator object <genexpr> at *>
repr
函数返回的结果,并非创建参数对象时使用的 i for i in range(3)
。此时,哪怕使用 repr
函数,对于程序员得到相同的对象也毫无帮助。
ascii
函数会返回 repr
函数返回结果的转义。它会将 repr
函数返回结果中、任何非 ascii 字符的内容,用转义序列 \x
、\u
和 \U
来进行替代。
我们还可以用“格式规格迷你语言”设置更多内容。
print("{0!s:-^9}".format(123)) #输出:---123---
通过输入 :
,再输入格式规格迷你语言,我们可以进行更多设置。
print("{0!s:=}".format(123))
!s
,则对于之后的设置 :=
而言,传入的参数就相当于 str(123)
,即'123'
——这是个字符串。从而任意仅对数字类型有效的选项在此时都会出错。我们可以添加以下的字符,来表示如何进行对齐:
字符 | 解释 |
---|---|
< |
左对齐 (这是大多数对象的默认值) |
> |
右对齐 (这是对数字的默认值) |
^ |
居中 |
= |
(仅对数字有效)在符号后填充 |
其中,=4
代表数字会被填充为 - 12
的形式——在符号后填充字符,而非右对齐时在符号左侧进行填充。
在指定了对齐方式后,我们可以在对齐方式的字符之前添加一个字符,指定以该字符进行填充。比如:$<
代表左对齐,不足的部分用 $
填充。
这些行为类似于之前学过的 字符串的操作:居中与对齐 的内容,与之前学过的内容类似,只有设置了字符串的长度、大于由 str.format
的参数得到的字符串的长度后,这里的设置才有用。
字符 | 解释 |
---|---|
+ |
除负数要添加减号外,在正数前也添加一个加号。 |
- |
仅在负数前添加减号。 |
|
(空格)在正数前添加一个空格作为符号。 |
我们可以设置数字前是否添加符号。详细见右表。
添加字符 z
以应用此选项。
在浮点数进行转换后,有可能会出现-0
、-0.0
之类的情况。这种情况一般是负的接近零的小数舍入导致的,也可能是转换了 float("-0")
。
如果添加了字符z
,Python 就会对这种情况进行修改。使其按照之前的符号选项,变为 0.0
、+0.0
或 (空格)0.0
。
添加字符 #
以应用此选项。
对于整数,如果之后设置了以二进制、八进制或十六进制进行输出。此选项会在输出值前添加0b
、0o
、0x
或0X
前缀。
对于浮点数和复数,它会使得转换结果总是包含小数点(即使转换结果不带小数部分)。
对于 g
或 G
转换,末尾的零不会从结果中被移除。
添加字符 0
以应用此选项。
在没有设置“对齐与填充选项”时。对于数字,如果应用此选项,相当于将之前的“对齐与填充选项”设置为 0=
,即“在符号后,用 0
进行填充”;而对于字符串或其它对象,相当于将填充字符设置为 0
。
如果设置了“对齐与填充选项”,则会优先使用“对齐与填充选项”中的设置。如果“对齐与填充选项”未指定填充字符,则该设置会将填充字符设置为 0
。
可以输入正整数来决定字符串的长度。如果字符串未达到长度,就会按照之前“对齐与填充选项”的设置,对字符串进行填充。
在分隔符这里有两个选项:,
和 _
,
选项仅对浮点数和表示类型为 n
的整数有效,它会将 ,
作为千位分隔符添加到整数或浮点数中。
_
选项对浮点数或表示类型为 n
的整数时,会将 _
作为千位分隔符添加到整数或浮点数中。而在使用二进制、八进制或十六进制输出的时候,会每四个数字插入一个下划线。
通过添加 .
再输入正整数,可以设置精度或字符串的最大长度。它仅对表示类型为 f
、F
或 g
、G
的浮点数,以及对字符串有效。
对于表示类型为 e
、E
或 f
、F
的浮点数,它代表小数点后应显示的数位。
对于表示类型为 g
或 G
的浮点数,它代表小数点前后总共显示的数位。
对于字符串,它代表字符串的最大大小。如果超出这个大小,就会截断之后的部分。
最后,我们可以设置表示类型。
字符串可使用的表示类型如下表:
选项 | 解释 |
---|---|
s |
(默认)作为字符串输出 |
(省略) | 使用默认选项 |
整数可使用的表示类型如下表:
选项 | 解释 |
---|---|
b |
用二进制格式输出 |
o |
用八进制格式输出 |
d |
(默认选项)用十进制格式输出,不支持分隔符。 |
x |
用十六进制格式输出,输出时使用小写字母,比如 1bf52 。
|
X |
用十六进制格式输出,输出时使用大写字母,比如 1BF52 。
|
n |
用十进制格式输出,支持分隔符。 |
c |
转换成对应的 ascii 字符进行输出。 |
(省略) | 使用默认选项 |
浮点数可使用的表示类型如下表:
选项 | 解释 |
---|---|
e |
用科学计数法输出,默认精度为6,即小数点后默认保留6位。比如1.000000e0 特别的,和精度无关,nan 会输出为 |
E |
用科学计数法输出,但是中间的字符会大写。nan 和 inf 也会被输出为大写。 |
f |
用定点表示法输出,默认精度为6,即小数点后默认保留6位。比如1.000000 特别的,和精度无关,nan 会输出为 |
F |
类似 f ,但 nan 和 inf 会被输出为大写。
|
g |
用科学计数法表示时,如果指数部分大于等于-4 而小于精度(默认精度为6),则会用定点表示法输出。反之,用科学计数法输出。特别的,和精度无关,nan 会输出为 |
G |
类似 g ,但输出会大写。
|
n |
这种方式的输出,按照 Python 文档的说明:“类似g ……”。由于 Python 文档不够详细,我们在下面对照示例和输出研究它的性质。
|
% |
转换成百分比格式进行输出。具体来讲,是将数字乘 100 ,然后用 f 格式输出,再在输出结果后添加一个百分号。
|
(省略) | 这种方式的输出,按照 Python 文档的说明:“类似g ……”。由于 Python 文档不够详细,我们在下面对照示例和输出研究它的性质。
|
以上表示类型还可以用于 decimal 对象。关于 decimal 类型,参见 Python 文档,这里不多叙述。关于这些表示类型对 decimal 对象的行为,请读者查阅 Python 文档进行补充。
n
的示例及输出表示类型 n
时,对齐方式和填充、符号选项、z
选项都会按照最平凡的方式产生作用。但分隔符、#
选项等选项的行为值得进行一些测试。我们看以下示例:
#默认设置
#默认是使用小写。
print("{:n}".format(1000.0)) #输出:1000
print("{:n}".format(12.345)) #输出:12.345
print("{:n}".format(float("-inf"))) #输出:-inf
print("{:n}".format(float("nan"))) #输出:nan
#分隔符
#print("{:,n}".format(1000.0)) #输出:ValueError: Cannot specify ',' with 'n'.
#print("{:_n}".format(1000.0)) #输出:ValueError: Cannot specify '_' with 'n'.
#保留符号
#无论如何,保留小数点后2位。
print("{:#n}".format(1000.0)) #输出:1000.00
print("{:#n}".format(1234.5)) #输出:1234.50
#精度
#如果数字 > 10**精度,则四舍五入后输出定点表示法。反之,则四舍五入后输出科学计数法。
print("{:.3n}".format(1000.0)) #输出:1e+03
print("{:.4n}".format(1000.0)) #输出:1000
print("{:.3n}".format(12.345)) #输出:12.3
print("{:.2n}".format(12.345)) #输出:12
print("{:.1n}".format(12.345)) #输出:1e+01
#默认精度
#默认精度为6。
print("{:n}".format(1234567.0)) #输出:1.23457e+06
#保留符号 + 精度
#在保留符号 + 精度时,总是使输出的数字位数等于精度。
print("{:#.2n}".format(1000.0)) #输出:1.0e+03
print("{:#.3n}".format(1000.0)) #输出:1.00e+03
print("{:#.4n}".format(1000.0)) #输出:1000.
print("{:#.5n}".format(1000.0)) #输出:1000.0
print("{:#.3n}".format(float("-0")))#输出:-0.00
省略表示类型时,对齐方式和填充、符号选项、z
选项以及单独的保留符号选项都会按照最平凡的方式产生作用。但分隔符、#
选项等选项的行为值得进行一些测试。我们看以下示例:
#默认设置
#默认设置使用小写
print("{}".format(1000.0)) #输出:1000.0
print("{}".format(12.345)) #输出:12.345
print("{}".format(float("-inf"))) #输出:-inf
print("{}".format(float("nan"))) #输出:nan
#分隔符
print("{:,}".format(1000.0)) #输出:1,000.0
print("{:_}".format(1000.0)) #输出:1_000.0
#精度
#如果数字 > 10**(精度+1),则四舍五入后输出定点表示法。反之,则四舍五入后输出科学计数法。
print("{:.4}".format(1000.0)) #输出:1e+03
print("{:.5}".format(1000.0)) #输出:1000.0
#默认精度
#默认精度足以精确表示输入的值。
print("{:}".format(1234567.0)) #输出:1234567.0
#保留符号 + 精度
#在保留符号 + 精度时,总是使输出的数字位数等于精度。
print("{:#.2}".format(1000.0)) #输出:1.0e+03
print("{:#.3}".format(1000.0)) #输出:1.00e+03
print("{:#.4}".format(1000.0)) #输出:1.000e+03
print("{:#.5}".format(1000.0)) #输出:1000.0
print("{:#.3}".format(float("-0")))#输出:-0.00
我们将利用这一章节的机会,简单学习 Python 文档中,语句语法的表现形式。在本章节所述的 Python 文档中,我们看到了以下的内容。
replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}"
field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
arg_name ::= [identifier | digit+]
attribute_name ::= identifier
element_index ::= digit+ | index_string
index_string ::= <any source character except "]"> +
conversion ::= "r" | "s" | "a"
format_spec ::= format-spec:format_spec
我们从第一行看起,第一行的内容为:
- 结构 replacement_field 由以下几个部分组成:
- 前缀
{
- 可选:结构 field_name
- 可选:
- 前缀
!
- 结构 conversion
- 前缀
- 可选:
- 前缀
:
- 结构 format_spec
- 前缀
- 后缀
}
- 前缀
而第二行的内容为:
- 结构 field_name 由以下几个部分组成:
- 结构 arg_name
- 任意个数的:
- 二选一(其一):
- 前缀
.
- 结构 attribute_name
- 前缀
- 二选一(其二):
- 前缀
[
- 结构 element_index
- 后缀
]
- 前缀
- 二选一(其一):
这种表示方法和正则表达式很相似。|
表示或、[]
表示之间的内容可选、""
表示之间的内容只是对应字符、而 ()*
表示中间的内容可以重复 0 至任意次。在倒数第三行,文档在 <>
中间添加了一个注释。
如果能直接读懂这些内容,读者就可以不依赖于文档中的语言描述,直接解析语句的语法。这对于文档中、语言描述不够明确的情况时很好用。
根据上面的文档,读者还可以自行设置一套格式化语法和方式。具体读懂上面的文档,可能需要读者学习之后的类章节之后才能做到。
除上面的文档外,还可以用 类的__format__方法 来自定义格式化语法。
f 字符串是一种创建字符串的方法。类似“原始字符串”在创建时就不进行转义、“f字符串”在创建时就立即被格式化。
value = "Python"
fstr1 = f"{value}"; print(fstr1) #输出:Python
fstr2 = f"{value * 2}"; print(fstr2) #输出:PythonPython
f 字符串会把 {}
之间的内容,当成普通的 Python 表达式来求值,把表达式返回的结果转换为字符串并输出到原字符串内。
在使用表达式时,有以下注意事项:
- 空表达式不被允许。
比如f"{}"
等,是不被允许的。 - 可以使用注释。
- 表达式中不能出现作为运算符的
=
。
=
符号只要不作为赋值表达式而出现,也是可以在表达式中的。比如:
fstr3 = f"{ '= '* 3 }"
print(fstr3) #输出:===
可以用以下方式使用注释:
fstr = f"{1 + 2 #输出:3}"
}"
print(fstr) #输出:3
在注释部分结束后,应当在下一行补一个 }"
(或 }'''
等,取决于创建 f 字符串时使用的引号)。
在表达式末尾,可以添加 =
。此时,会在 f 字符串中同时输出表达式的字符与表达式的结果。比如:
value = "Python"
fstr1 = f"{value = }"
print(fstr1)
fstr2 = f"{value * 2=}"
print(fstr2)
输出为:
value = 'Python'
value * 2='PythonPython'
等号前后以及表达式中的空格都会被原样输出。
在= 标记的后面,可以设置把“表达式返回值”转换为字符串的方式。具体有三种转换方式:
!s
(默认)用str()
转换。!r
(默认)用repr()
转换。!a
(默认)用ascii()
转换。
与 str.format 类似,这里也可以使用格式规格迷你语言。在 str.format 中使用格式规格迷你语言的方法可以完全照搬到这里。
但在 str.format 之外,这里使用的格式规格迷你语言可以“按替换规则设置”。比如:
width = 6
precision = 4
value = 114.514
fstr = f"{value:@>{width}.{precision}}"
print(fstr) 输出:@114.5
在解析时,Python 先按规则替换 {width}
和 {precision}
的内容,再替换 {value:@>6.4}
的内容。
特别的,{{
会被替换为 {
,而 }}
会被替换为 }
。比如:
fstr = f"}} {{}} {1+2:$^{1+2}}"
print(fstr)
输出为:
} {} $3$
如同上述示例,最后的 }}
分别与前面两个单独的 {
配对。如果有配对,就不会按转义规则进行替换。
这种方法很像是 C 语言中 printf 函数格式化字符串的方式。这种方法可处理的类型范围较窄,并且更难以正确使用,但更为快速。
字符串和元组 / 字典之间支持一种特殊的二元运算:格式化运算,其运算符是%
。
示例:
s = "%(a)s %(b)s %(c)s"
d = {"a":"Py", "b":"th", "c":"on"}
print(s % d) #输出:Py th on
s = "%s %s %s"
t = ("Py", "th","on")
print(s % t) #输出:Py th on
字符串中,被替换的部分由以下几个部分组成:
部分 | 示例 | 解释 |
---|---|---|
% | % |
字符% 。
|
键 (可选) |
(key_name) |
用括号括起的、字典键的名称。 |
转换旗标 (可选) |
此处列举的旗标可以组合。 | |
# |
转换数字时,使用“替代形式”(见“转换类型”) | |
0 |
转换数字时,用0填充 | |
- |
转换值靠左对齐。如果同时给出旗标0 ,则覆盖后者。比如旗标 -0 等同于旗标- ,而旗标0- 等同于旗标0 。
| |
|
(空格旗标)正数或空字符串前会留一个空格 | |
+ |
转换数字时,前面一定有符号。 如果和空格旗标一起使用,此旗标产生的符号字符会覆盖空格旗标的效果。 | |
最小宽度 (可选) |
* |
由元组中、下一个元素的数值决定。 如果使用字典与字符串作格式化运算,则不能使用此项。 |
42 |
任意非负整数 | |
精度 (可选) |
.42 |
由. 开头的、任意非负整数
|
.* |
由元组中、下一个元素的数值决定。 如果使用字典与字符串作格式化运算,则不能使用此项。 | |
长度修饰符 (可选) |
h |
长度修饰符可以为h 、l 或L 设置此项仅为了和 printf 函数一致,没有任何作用。 |
转换类型 (必须) |
s |
这里给出的内容决定了内容转换为字符串的方式。 |
转换符 | 含意 | 替代形式 |
---|---|---|
d |
有符号十进制整数。 | |
i |
有符号十进制整数。 | |
o |
有符号八进制数。 | 以0o 开头
|
u |
过时类型 -- 等价于 d 。 |
|
x |
有符号十六进制数(小写)。 | 以0x 开头
|
X |
有符号十六进制数(大写)。 | 以0X 开头
|
e |
浮点指数格式(小写)。 | (无论小数点后有没有数值) 总是包含小数点。 |
E |
浮点指数格式(大写)。 | |
f |
浮点十进制格式。 小数点后的数码位数由精度决定,默认为 6。 | |
F |
浮点十进制格式。 小数点后的数码位数由精度决定,默认为 6。 | |
g |
浮点格式。 如果指数小于 -4 或不小于精度,则等同于 e ,否则,等同于 f 。
|
末尾的零不会被移除。 (无论小数点后有没有数值) 总是包含小数点。 |
G |
浮点格式。 如果指数小于 -4 或不小于精度,则等同于 E ,否则,等同于 F 。
| |
c |
单个字符(接受整数或单个字符的字符串)。 | |
r |
repr() 如果指定精度,则输出会被截短至精度 |
|
s |
str() 如果指定精度,则输出会被截短至精度 |
|
a |
ascii() 如果指定精度,则输出会被截短至精度 |
|
% |
不转换参数,在结果中输出一个 % 字符。 |