1术语描述
- str.format()方法通过字符串中的花括号{}来识别替换字段(
replacement field
),从而完成字符串的格式化。 - 替换字段:由字段名(
field name
)和转换字段(conversion field
)以及格式说明符(format specifier
)组成,即一般形式为{字段名!转换字段:格式说明符}
。 - 字段名:分为简单字段名(
simple field name
)和复合字段名(compound field name
)。 - 而转换字段和格式说明符都是可选的。
2简单字段名
2.1 简单字段名的说明
- 简单字段名有三种写法:
- 省略不写
{}
- 数字
{十进制非负整数}
- 变量名
{合法的Python标识符}
2.2 省略字段名
- 花括号内省略字段名,传递位置参数。
- 替换字段形式:
{}
- 注意:花括号个数可以少于位置参数的个数,反之会报错。
下面来看一个示例
# 省略字段名传递位置参数
print('小孩:{},我要吃{}。'.format('妈妈','柚子'))
# 花括号个数可以少于位置参数的个数
print('妈妈:要得{},吃几个,两个够吗?'.format('幺儿', '吃', '够吗?'))
# 花括号个数多于位置参数的个数则会报错
# print('妈妈:要得{},吃几个,两个{}'.format('幺儿'))
# IndexError: Replacement index 1 out of range for positional args tuple
2.3 数字形式的简单字段名
可以通过数字形式的简单字段名传递位置参数。
- 数字必须是大于等于0的整数。
- 带数字的替换字段可以重复使用。
- 数字形式的简单字段名相当于把 format 中的所有位置参数整体当作一个元组,通过字段名中的数字进行取值。
- 即 {0} 等价于 tuple[0],所以花括号内的数字不能越界。
下面来看一个示例
# 通过数字形式的简单字段名传递位置参数
print('小孩:{0},我要吃{1}。'.format('妈妈','柚子'))
# 数字形式的简单字段名可以重复使用
print('妈妈:要得幺儿{0}吃几个{0}两个够吗?'.format(','))
# 体会把所有位置参数整体当成元组来取值
print('小孩:够了,谢谢妈妈,妈妈真好。我还要吃{0}、{1}、{4}、{3}、{2}'.format('榴莲', '臭豆腐', '皮蛋', '鲱鱼罐头', '螺狮粉'))
# 尝试一下越界错误
# print('{1}'.format('错误用法'))
# IndexError: tuple index out of range
2.4 变量名形式的简单字段名
使用变量名形式的简单字段名传递关键字参数。
- 关键字参数的位置可以随意调换。
下面来看一个例子
# 使用变量名形式的简单字段名传递关键字参数
print('{sakura}:{naruto},你一定要把{sasuke}带回来。'.format(sakura='小樱', naruto='鸣人', sasuke='佐助'))
# 关键字参数的顺序可以随意调换
print('{sakura}:{naruto},你一定要把{sasuke}带回来。'.format(naruto='鸣人', sakura='小樱', sasuke='佐助'))
2.5 简单字段名的混合使用
- 混合使用数字形式和变量名形式的字段名,可以同时传递位置参数和关键字参数。
- 关键字参数必须位于位置参数之后。
- 混合使用时可以省略数字。
- 省略字段名
{}
不能和数字形式的字段名{非负整数}
同时使用。
下面来看一个示例
# 混合使用数字形式和变量名形式的字段名
# 可以同时传递位置参数和关键字参数
print('这是一个{0},开始很{1},结局很{ending}。'.format('冷笑话', '恐怖', ending='凄惨'))
# 但是关键字参数必须位于位置参数之后
# print('这是一个{0},开始很{1},结局很{ending}。'.format('冷笑话', ending='凄惨', '恐怖'))
"""
SyntaxError: positional argument follows keyword argument
"""
# 数字也可以省略
print('这是一个{},开始很{},结局很{ending}。'.format('冷笑话', '恐怖', ending='凄惨'))
# 但是省略字段名不能和数字形式的字段名同时出现
# print('这是一个{0},开始很{},结局很{ending}。'.format('冷笑话', '恐怖', ending='凄惨'))
"""
ValueError: cannot switch from automatic field numbering to manual field specification
"""
2.6 使用元组和字典传参
str.format()方法还可以使用*元组
和**字典
的形式传参,两者可以混合使用。位置参数、关键字参数、*元组
和**字典
也可以同时使用,但是要注意,位置参数要在关键字参数前面,*元组
要在 **字典
前面。
下面我们来看一个示例
# 使用元组传参
infos = '钢铁侠', 66, '小辣椒'
print('我是{},身价{}亿。'.format(*infos))
print('我是{2},身价{1}亿。'.format(*infos))
# 使用字典传参
venom = {'name': '毒液', 'weakness': '火'}
print('我是{name},我怕{weakness}。'.format(**venom))
# 同时使用元组和字典传参
hulk = '绿巨人', '拳头'
captain = {'name': '美国队长', 'weapon': '盾'}
print('我是{}, 我怕{weapon}。'.format(*hulk, **captain))
print('我是{name}, 我怕{1}。'.format(*hulk, **captain))
# 同时使用位置参数、元组、关键字参数、字典传参
# 注意:
# 位置参数要在关键字参数前面
# *元组要在**字典前面
tup = '鹰眼',
dic = {'weapon': '箭'}
text = '我是{1},我怕{weakness}。我是{0},我用{weapon}。'
text = text.format(*tup, '黑寡妇', weakness='男人', **dic)
print(text)
3. 复合字段名
3.1 复合字段名的说明
- 同时使用了数字和变量名两种形式的字段名就是复合字段名。
- 复合字段名支持两种操作符:
[]
方括号
.
点号
3.2 .
点号的使用
3.2.1 传递位置参数
- 替换字段形式:
{数字.属性名}
- 只有一个替换字段的时候可以省略数字
下面来看一个例子
# 复合字段名中使用点号传递对象属性
class Person:
def __init__(self, name, addr):
self.name = name
self.addr = addr
p = Person('辣妹子', '重庆')
# 点号用法:传递位置参数
print('我是{0.name},家在{0.addr}。'.format(p))
# 当只有一个替换字段的时候可以省略数字
print('{.name}辣!'.format(p))
3.2.2 传递关键字参数
- 替换字段形式:
{关键字参数名.属性名}
下面来看一个例子
# 点号用法:传递关键字参数
print('我是{girl.name},家在{girl.addr}。'.format(girl=p))
# 和上一句等价
print('我是{p.name},家在{p.addr}。'.format(p=p))
3.3 [] 方括号的使用
3.3.1 传递位置参数
- 用列表传递位置参数
- 用元组传递位置参数
- 用字典传递位置参数
下面来看一个示例
# 方括号用法:用列表传递位置参数
infos = ['阿星', 9527]
food = ['霸王花', '爆米花']
print('我叫{0[0]},警号{0[1]},爱吃{1[0]}。'.format(infos, food))
# 方括号用法:用元组传递位置参数
food = ('僵尸', '脑子')
print('我叫{0[0]},年龄{1},爱吃{0[1]}。'.format(food, 66))
# 方括号用法:用字典传递位置参数
dic = dict(name='阿星', pid=9527)
print('我是{[name]}!'.format(dic))
# 多个替换字段,不能省略数字
print('我是{0[name]},警号{0[pid]}。'.format(dic))
3.3.2 传递关键字参数
- 用列表传递关键字参数
- 用元组传递关键字参数
- 用字典传递关键字参数
# 方括号用法:传递关键字参数
names = ['皮卡丘']
dic = {'name': '妙蛙花'}
skills = ('十万伏特', '飞叶快刀')
text = '我是{names[0]},我会{skills[0]}。我是{dic[name]},我会{skills[1]}。'
text = text.format(names=names, skills=skills, dic=dic)
print(text)
4. 转换字段
转换字段 conversion field
的取值有三种,前面要加 !
:
- s:传递参数之前先对参数调用 str()
- r:传递参数之前先对参数调用 repr()
- a:传递参数之前先对参数调用 ascii()
ascii() 函数类似 repr() 函数,返回一个可以表示对象的字符串。但是对于非 ASCII 字符,使用 \x,\u 或者 \U 转义。
看下面一个示例
name = "洛克·李"
print('I am {!s}!'.format(name))
print('I am {!r}!'.format(name))
print('I am {!a}!'.format(name))
"""
输出结果:
I am 洛克·李!
I am '洛克·李'!
I am '\u6d1b\u514b\xb7\u674e'!
"""
5. 格式说明符
- 在替换字段中,格式说明符前面有一个冒号
:
{字段名!转换字段:格式说明符}
- 其中格式说明符本身可以是一个字段名,比如:
print('{0:{1}}'.format(3.14159, '.4f'))
"""
3.1416
"""
5.1 标准格式说明符的格式
如果不通过重写__format__
方法来进行自定义的话,标准格式说明符的形式如下。其中方括号是可选的。
[[fill]align][sign][#][0][width][grouping_option][.precision][type]
中文形式可以写作:
[[填充]对齐方式][正负号][#][0][宽度][分组选项][.精度][类型码]
为了方便后面使用,这个我们先将类型码列举出来
类型 | 说明 |
---|---|
b | 二进制 |
c | character,即ascii码所对应的字符 |
d | 十进制 |
e | 科学记数法(e) |
E | 科学记数法(E) |
f | 浮点数,默认保留小数点后六位,四舍五入 |
F | 与浮点数一样,只是会将nan转换成NAN,inf转换成INF |
g | 当整数部分大于六位有效数字时,采用科学记数法(e);反之,保留六位有效数字,五舍六入 |
G | 当整数部分大于六位有效数字时,采用科学记数法(E);反之,保留六位有效数字,五舍六入 |
n | 跟 g 一样,只不过用本地化的分隔符来分隔数字 |
s | 字符串 |
o | 八进制 |
x | 十六进制(abcdef) |
X | 十六进制(ABCDEF) |
% | 百分比 |
5.2 填充与对齐方式
填充:
- 只能是一个字符
- 不指定默认用空格填充
- 如果指定填充字符,则必须要同时指定对齐方式
对齐方式的取值:
<
:左对齐>
:右对齐^
:居中=
:在正负号(如果有的话)和数字之间填充,该对齐选项仅对数字类型有效。它可以输出类似+0000120
这样的字符串。
注意:如果不给定最小宽度 width
,对齐方式毫无意义。
5.3 正负号
正负号选项仅对数字类型
生效
取值有三种:
+
正数前面添加正号,负数前面添加负号-
仅在负数前面添加负号(默认行为)空格
:正数前面需要添加一个空格,以便与负数对齐
# 正负号
print('{:填=+8.2f}'.format(3.14159))
print('{:充=+8.2f}'.format(-3.14159))
print('{:填=+8.2f}'.format(0))
print('{:充=+8.2f}'.format(-0))
"""
输出结果
+填填填3.14
-充充充3.14
+填填填0.00
+充充充0.00
"""
5.4 # 号
#
号:
#b
给二进制数加上0b
前缀#o
给八进制数加上0o
前缀#x
给十六进制数加上0x
前缀#X
给十六进制数加上0X
前缀
比如:
# 二进制
print('二进制:{:>#20b}'.format(11))
# 八进制
print('八进制:{:>#20o}'.format(11))
# 十六进制
print('十六进制:{:>#18x}'.format(11))
print('十六进制:{:>#18X}'.format(11))
"""
输出结果
二进制: 0b1011
八进制: 0o13
十六进制: 0xb
十六进制: 0XB
"""
5.5 最小宽度
最小宽度 width
:
- 如果不指定,最小字段宽度由内容决定,与内容相等(即:不填充)
- 如果最小宽度前面有一个前导
0
,意味着用0
填充,这等价于指定了0=
的填充和对齐方式
比如:
print('{:=04}'.format(11))
# 等价于
print('{:0=4}'.format(11))
5.6 分组选项
分组选项 grouping_option
的取值有两种:
- 逗号
,
:使用逗号对数字以千为单位进行分隔。n
类型的数字可以使用本地化的分隔符。
n
类型在本机无法使用分组选项,
# n 类型使用本地化的分组选项 ,
# print('数字:{0:,n}'.format(6666))
"""
ValueError: Cannot specify ',' with 'n'.
"""
# 使用 d 类型确实是可以的
print('数字:{:,d}'.format(587666))
"""
输出结果
587,666
"""
- 下划线
_
:使用下划线对浮点数和d
类型的整数以千为单位进行分隔。对于 b、o、x 和 X 类型,每四位插入一个下划线,其他类型都会报错。
# 分组选项 _ 作用于 b 类型
print('数字:{0:_b}'.format(0b100111011))
"""
数字:1_0011_1011
"""
# 分组选项 _ 作用于 o 类型
print('数字:{0:_o}'.format(0o426754316))
"""
数字:4_2675_4316
"""
# 分组选项 _ 作用于 x 类型
print('数字:{0:_x}'.format(0x2a846e98d))
"""
数字:2_a846_e98d
"""
# 分组选项 _ 作用于 X 类型
print('数字:{0:_X}'.format(0X2a846e98d))
"""
数字:2_A846_E98D
"""
# 分组选项 _ 作用于其他类型(比如 n 类型)
# print('字符串:{0:_n}'.format(1234567))
"""
ValueError: Cannot specify '_' with 'n'.
"""
5.7 精度
- 精度指定了小数点后面要展示多少位小数
- 对于非数字类型,精度指定了最大字段宽度
- 整数类型不能指定精度
# 对于非数字类型,精度指定最大字段宽度
print('{0:.3}'.format('妈妈,我要吃柚子'))
"""
妈妈,
"""
# 整数类型不能指定精度
# print('{:.3d}'.format(666))
"""
ValueError: Precision not allowed in integer format specifier
"""
5.8 类型码
类型码可以分为三大类:
- 字符串类型
- 整数类型
- 浮点数类型
5.8.1 字符串类型
s
字符串类型。这是字符串的默认类型,可以省略。None
不指定类型。同s
类型。
# s 类型
print('{0:s}'.format('妈妈,我要吃柚子'))
# s 类型可以省略
print('{0:}'.format('妈妈,我要吃柚子'))
5.8.2 整数类型
b
二进制。c
字符。把整数转换为相应的 Unicode 字符,然后再打印。d
十进制整数。o
八进制数。x
十六进制数,a 到 f 小写。X
十六进制数,A 到 F 大写。None
不指定类型,与 d 相同。
# b 类型:二进制
print('{0:b}'.format(97))
# c 类型:把整数转换成 unicode 字符
print('{:c}'.format(97))
# d 类型:十进制整数
print('{:d}'.format(97))
print('{:}'.format(97))
# o 类型:八进制数
print('{:o}'.format(97))
# x 类型:十六进制数,a到f小写
print('{:x}'.format(97))
# X 类型:十六进制数,A到F大写
print('{:X}'.format(97))
"""
1100001
a
97
97
141
61
61
"""
5.8.3 浮点数类型
e
科学记数法,用e
来表示指数。默认精度为 6 位E
与e
相同,但是使用大写的E
表示指数。f
定点记法,默认精度为 6。F
定点记法,同f
,但是会把nan
转换成NAN
,把inf
转换成INF
g
通用general
格式。自动转换到e
或者f
格式,具体的转换规则在此省略。正无穷、负无穷、正零、负零和非数字分别显示为inf
,-inf
,0
,-0
,nan
。指定精度为 0 时等价于精度为 1。默认精度为 6 位。G
通用general
格式。自动转换到E
或者F
格式,转换规则同上,相应表示方式换成大写。n
数字number
类型。跟g
一样,只不过用本地化的分隔符来分隔数字。%
百分号类型。会将数字乘以 100,然后以f
定点fixed-point
格式显示,最后加上一个百分号%
。None
不指定类型。输出效果类似调用str()
函数。
# e 类型
print('{:e}'.format(1234567.1234567))
# E 类型
print('{:E}'.format(1234567.1234567))
# 修改精度为 10 位
print('{:.10E}'.format(1234567.1234567))
# f 类型
print('{:f}'.format(1234567.1234567))
# F 类型
nan = float('nan')
inf = float('inf')
print('{:F}\n{:F}'.format(nan, inf))
# g 类型
print('{:g}'.format(1234567.1234567))
print('{:g}'.format(1234.1234))
# G 类型
print('{:G}'.format(1234567.1234567))
print('{:G}'.format(1234.1234))
# n 类型
print('{:n}'.format(1234567.1234567))
print('{:n}'.format(1234.1234))
# % 类型
print('{:%}'.format(1))
"""
1.234567e+06
1.234567E+06
1.2345671235E+06
1234567.123457
NAN
INF
1.23457e+06
1234.12
1.23457E+06
1234.12
1.23457e+06
1234.12
100.000000%
"""
6. 补充说明
- 输出花括号需要用花括号本身来转义
# 打印花括号需要使用花括号转义
print('{{我在{}等你}}'.format('老九学堂'))
"""
{我在老九学堂等你}
"""
- 对象可以自定义格式说明符来替换标准格式说明符,比如
datetime
类。
from datetime import datetime
print("今天是: {0:%Y-%m-%d %H:%M:%S}".format(datetime.now()))
"""
今天是: 2020-01-02 11:48:34
"""