Python详解 %、format、f-string (格式化字符串 -用法总结)

目录

一、绪论

二、古代方法 —— %

2.1 示例

2.2 速查表

三、近代方法 —— format

3.1 示例

3.2 速查表 (数字格式化)

四、现代方法 —— f-string

4.1 说明

4.2 示例


一、绪论

随着 Python 的更新,其字符串格式化功能也在不断地优化。在 Python 2.6 以前,% 操作符一枝独秀;在 Python 2.6 之后,迎来后起之秀 format 操作符;在 Python 3.6 之际,更有 f-string 操作符异军突起。字符串格式化的逐步优化,不但简化了操作,而且提升了效率,如下执行时间对比柱状图所示:

可见,远古时期的模板字符串 str.Template 过于低效,str.format 和 % 操作符在简便和效率之间有所权衡,而 f-string 则具有最佳性能。以下将逐一回顾:

二、古代方法 —— %

2.1 示例

作为最古老格式化字符串方式,% 操作符与 C 中 sprintf 函数的语法基本相同,因而具有浓浓的 C 风味,使得最终的表达式可能较为复杂和冗长。关于 % 操作符的用法一言以蔽之 —— 在目标字符串中指定位置插入待填充内容的类型符号。例如:

1
2
3
4
5
6
7
>>> "%s is %d years old." % ('Zhang San', 18)  # 直接使用
'Zhang San is 18 years old.'
# -------------------------------------------------------------------------
>>> name = 'Zhang San'
>>> age = 18
>>> "%s is %d years old." % (name, age)  # 间接使用
'Zhang San is 18 years old.'

2.2 速查表

常用类型符号及其说明:

符号 说明
%s 格式化字符串
%c 格式化字符及其 ASCII 码
%d 格式化整数
%u 格式化无符号整型
%o 格式化无符号八进制数
%x 格式化无符号十六进制数
%X 格式化无符号十六进制数 (大写)
%f 格式化浮点数,可指定小数点后的精度
%e 格式化浮点数 (科学计数法)
%E 同 %e
%g %f 和 %e 的简写
%G %F 和 %E 的简写
%p 用十六进制数格式化变量的地址

辅助指令:

符号 说明
* 定义宽度或小数点精度
- 用于左对齐
+ 正数前显示加号 '+'
正数前显示空格
# 八进制数前显示零 '0'、十六进制数前显示 '0x' 或 '0X'
0 显示的数字前面填充'0'而不是默认的空格
% '%%' 输出一个 '%' (百分号转义)
(var) 映射变量 (字典参数)
m.n. m 是显示的最小总宽度,n 是小数点后的保留位数

三、近代方法 —— format

3.1 示例

相比于 % 操作符,str.format() 在少量降低性能的条件下,强化了字符串格式化的功能,其基本语法是接受一个格式字符串及任意一组位置与关键字参数,通过 { } 和 : 来代替 %,而且参数数量没有限制 (相对概念),参数位置可以不按顺序。例如:

1
2
3
4
5
6
7
8
>>> "{} is {} years old.".format('Li Hua', 3)  # 不指定位置索引, 默认排列
'Li Hua is 3 years old.'
# -------------------------------------------------------------------------
>>> "{0} is {1} years old.".format('Li Hua', 3)  # 指定位置索引
'Li Hua is 3 years old.'
# -------------------------------------------------------------------------
>>> "{0} is {1}{1} years old.".format('Li Hua', 3)  # 指定位置索引, 多次使用
'Li Hua is 33 years old.'

还可以对 str.format() 设定参数:(其中使用 * 和 ** 解包的原理详见文章 单/双星号操作符)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> "宿舍楼:{building}, 门牌号:{number}".format(building="鹏翔2斋", number=1234)
'宿舍楼:鹏翔2斋, 门牌号:1234'
# -------------------------------------------------------------------------
>>> info = {"building":"鹏翔2斋", "number":1234}
>>> "宿舍楼:{building}, 门牌号:{number}".format(**info)  # 使用 ** 对 dict 解包
'宿舍楼:鹏翔2斋, 门牌号:1234'
# -------------------------------------------------------------------------
>>> info2 = ("鹏翔2斋", 1234)
>>> "宿舍楼:{}, 门牌号:{}".format(*info)  # 使用 * 对 tuple 解包
'宿舍楼:鹏翔2斋, 门牌号:1234'
# -------------------------------------------------------------------------
>>> info3 = ["鹏翔2斋", 1234]
>>> "宿舍楼:{0[0]}, 门牌号:{0[1]}".format(info3)  # 使用 index 指定 list 元素
'宿舍楼:鹏翔2斋, 门牌号:1234'

甚至可以向 str.format() 传入对象:

1
2
3
4
5
6
7
>>> class Num:
    def __init__(self, num):
        self.num = num

>>> six = Num(6)
>>> "my number is {0.num}".format(six)  # 调用成员属性作为参数
'my number is 6'

以上实例足以说明 str.format() 的灵活与强大!

3.2 速查表 (数字格式化)

以下仅列举常见用法示例及其说明:

示例 格式 结果 说明
3.1415 {:.2f} 3.14 保留小数点后2位
2.7182 {:+.2f} +2.71 带符号保留小数点后2位
-1 {:+.2f} -1.00 带符号保留小数点后2位
1.2345 {:.0f} 1 删去小数部分
8 {:x>2d} 08 整数补x (填充左边, 宽度为2)
16 {:y<4d} 16xx 整数补y (填充右边, 宽度为4)
1000000 {:,} 1,000,000 , 作千分位分隔符
1000000 {:_} 1_000_000 _ 作千分位分隔符
0.48 {:.4%} 48.0000% 小数转百分比
100000000 {:.2e} 1.00e+08 指数记法
16 {:>8d} 16 向右对齐 (宽度为8)
16 {:<8d} 16 向左对齐 (宽度为8)
16 {:^8d} 16 居中对齐 (宽度为8)

还有进制转换:(# 符号用于切换数字显示,可获取各进制的带前缀大、小写表示)

示例 格式 结果 说明
12 {:#b} 0b1100 带前缀 二进制小写表示
12 {:b} 1100 二进制表示
12 {:o} 14 八进制表示
12 {:d} 12 十进制表示
12 {:x} c 十六进制表示
12 {:#X} 0XC 带前缀 十六进制大写表示

事实上,str.format() 的用法远比上述示例复杂多样,详见官方文档:格式字符串语法。

四、现代方法 —— f-string

4.1 说明

相较于前两种方法,Python 3.6 中的 f-string (格式化字符串常量 - formatted string literals),应为最佳形式 —— 简洁、易读,不仅有助于简化十分繁琐的写法,而且具有最佳效率,非常建议使用。与具有恒定值的其它字符串常量不同,格式化字符串实际上是运行时运算求值的表达式。

f-string 的语法结构为:

1
f ' <text> { <expression> <optional !s, !r, or !a> <optional : format specifier> } <text> ... '

其中,f (或 F) 为目标字符串前缀; 表示占位符的上下文;类似于 str.format,目标字符串中的占位符 (一种运行时计算的表达式) 也使用了花括号 { },在其中必须加入表达式 ,可选参数标志 !s 表示调用表达式上 str() (默认)、!r 表示调用表达式的 repr()、!a 表示调用表达式的 ascii()。最后,使用 format 协议格式化目标字符串。

4.2 示例

其常用方法是在目标字符串前加上字母 f F,然后在目标字符串中插入占位符(直接在 { } 中加入变量)。例如:

1
2
3
4
>>> name = 'Bob'
>>> age = 24
>>> f"{name} is {age} years old."  # 格式字符串前缀 f 或 F, 中间插入占位符 {variable}
'Bob is 24 years old.'

其实,花括号 { } 中可以灵活地填入各种表达式,甚至可以调用函数与类成员等,而不仅仅局限于变量,例如:

1
2
3
4
5
6
7
8
9
>>> f"The result of the function is {2 * 3 + 4}"  # 算术表达式 内联运算
'The result of the function is 9.43656365691809'
# ----------------------------------------------------------------------------
>>> f'My weapen is {weapen.upper()}'  # 函数/方法调用
'My weapen is AK47'
# ----------------------------------------------------------------------------
>>> import math
>>> f"The result of the function is {2 * math.e + 4}"  # 类成员(属性/方法)调用
'The result of the function is 9.43656365691809'

当然,除了内置函数和自定义普通函数,lambda 表达式 (匿名函数) 也可以填入 { },但为避免 lambda 表达式的分号 : 带来的歧义,应将 lambda 表达式置于圆括号 ( ) 中,例如:

1
2
3
4
5
>>> f'The test result is {lambda x: x + 1 (99)}'  # 无法解析 End-of-File
SyntaxError: unexpected EOF while parsing
# ----------------------------------------------------------------------------
>>> f'The test result is {(lambda x: x + 1)(99)}'  # 格式:{(lambda表达式)(lambda表达式参数)}
'The test result is 100'

为避免引号冲突,可以灵活使用 单引号 ' 、双引号 '' 、单引号文本字符串 ' ' '、双引号文本字符串 '' '' '' 来解决,只需保证目标字符串外定界符的引号 与 目标字符串中占位符内的引号不同。例如:

1
2
3
4
5
6
7
8
9
10
>>> f"We are {"CT"}"  # 内外均使用了双引号 ""
SyntaxError: invalid syntax  
# ----------------------------------------------------------------------------
>>> f"We are {'CT'}"  # 内用单引号 '' 外用双引号 ""
'We are CT'
# ----------------------------------------------------------------------------
>>> f'''We are {'CT'} and you are {"T"} '''  # 内用单双 外用三
'We are CT and you are T '

# 不论怎么搭配, 只要内外不冲突即可

但注意,占位符 { } 内不能使用转义符号 \ 对引号转义 (因为不允许出现 \,除非用变量间接传递),而上下文可以:

1
2
3
4
5
6
7
8
9
>>> f'I think {you\'re powerful}'  # {} 内不可使用转义符号 \
SyntaxError: f-string expression part cannot include a backslash
# ----------------------------------------------------------------------------
>>> f'You\'re powerful'  # {} 外则可同正常字符串一样使用转义符号 \
"You're powerful"
# ----------------------------------------------------------------------------
>>> words = "you\'re powerful"  # 除非用变量传递
>>> f'I think {words}'
"I think you're powerful"

此外,更多技术细节详见官方文档。由于 f-string 是基于 str.format 发展而来的, str.format 所用符号 同样适用于 f-string (所以可以使用 3.2 节的速查表)。


参考文献:

https://docs.python.org/zh-cn/3.6/library/string.html#formatstrings

https://www.runoob.com/python/att-string-format.html

https://www.runoob.com/python/python-strings.html

https://www.jianshu.com/p/72cc021cf30a

https://zhuanlan.zhihu.com/p/79826445

https://docs.python.org/3/reference/lexical_analysis.html#f-strings