Python 3.6+ 必备:f-string 详解 – wiki基地


Python 3.6+ 必备利器:f-string 全方位详解

在 Python 的世界里,字符串格式化一直是一个不断演进的话题。从古老的 % 操作符,到后来功能更强大的 str.format() 方法,开发者们一直在寻求更简洁、更直观、更高效的方式来构建包含动态内容的字符串。终于,在 Python 3.6 版本中,一个革命性的特性横空出世,它就是 格式化字符串字面量(Formatted String Literals),通常被亲切地称为 f-string

f-string 的出现,不仅仅是增加了一种新的语法糖,它凭借其无与伦比的简洁性、可读性和出色的性能,迅速成为了 Python 社区推荐的主流字符串格式化方式。对于任何使用 Python 3.6 或更高版本的开发者来说,熟练掌握 f-string 几乎是一项必备技能。本文将深入浅出,全方位地详解 f-string 的方方面面,从基础用法到高级技巧,助你彻底掌握这一现代 Python 开发的利器。

一、 回顾:Python 字符串格式化的演变之旅

在深入了解 f-string 之前,让我们简要回顾一下 Python 中字符串格式化的历史,这有助于我们理解 f-string 为何如此受欢迎。

1. 百分号 % 操作符 (printf-style)

这是 Python 最早引入的字符串格式化方式,借鉴了 C 语言的 printf 函数风格。

“`python
name = “Alice”
age = 30
height = 1.65

使用 % 操作符

formatted_string = “Name: %s, Age: %d, Height: %.2f” % (name, age, height)
print(formatted_string)

输出: Name: Alice, Age: 30, Height: 1.65

字典形式

data = {“name”: “Bob”, “age”: 25}
formatted_string_dict = “Name: %(name)s, Age: %(age)d” % data
print(formatted_string_dict)

输出: Name: Bob, Age: 25

“`

缺点:

  • 类型匹配要求严格: %d 只能用于整数,%s 用于字符串,%f 用于浮点数等,如果类型不匹配,会抛出 TypeError
  • 可读性较差: 当需要格式化的变量较多时,格式字符串和后面的元组/字典需要来回对应,容易出错且不直观。
  • 表达能力有限: 难以在格式说明符中直接进行复杂的表达式计算。

2. str.format() 方法 (自 Python 2.6 起)

为了克服 % 操作符的缺点,Python 引入了 str.format() 方法,提供了更灵活、更强大的格式化功能。

“`python
name = “Charlie”
age = 35
city = “New York”

使用位置参数

formatted_string_pos = “Name: {}, Age: {}, City: {}”.format(name, age, city)
print(formatted_string_pos)

输出: Name: Charlie, Age: 35, City: New York

使用编号参数

formatted_string_num = “Name: {0}, Age: {1}, City: {0} lives in {2}”.format(name, age, city)
print(formatted_string_num)

输出: Name: Charlie, Age: 35, City: Charlie lives in New York

使用关键字参数

formatted_string_key = “Name: {n}, Age: {a}, City: {c}”.format(n=name, a=age, c=city)
print(formatted_string_key)

输出: Name: Charlie, Age: 35, City: New York

混合使用与格式说明符

pi = 3.1415926
formatted_string_spec = “Pi approx: {p:.3f}, Name: {n!r}”.format(p=pi, n=name) # !r 调用 repr()
print(formatted_string_spec)

输出: Pi approx: 3.142, Name: ‘Charlie’

“`

优点:

  • 更灵活: 支持位置参数、关键字参数、编号参数,以及它们的混合使用。
  • 可读性提升: 使用 {} 占位符,特别是配合关键字参数时,可读性优于 %
  • 功能更强: 格式说明符(Format Specification Mini-Language)更加强大和统一,支持对象属性访问、索引访问等。

缺点:

  • 仍然有些冗长: 每次都需要调用 .format() 方法,并将变量作为参数传入,代码显得不够紧凑。
  • 变量名重复: 使用关键字参数时,变量名往往需要在字符串模板和 .format() 调用中各写一次。

二、 f-string 横空出世:简洁、直观、高效

f-string 的设计目标就是解决 str.format() 的冗长问题,同时保持甚至超越其强大的功能。它的核心理念是将表达式直接嵌入到字符串字面量中。

1. 基本语法

f-string 的语法非常简单:在字符串字面量的引号(单引号、双引号、三引号皆可)前加上一个 fF 前缀即可。字符串内部使用花括号 {} 包裹需要嵌入的 Python 表达式。

“`python
name = “David”
age = 40

基本 f-string 用法

f_string = f”My name is {name} and I am {age} years old.”
print(f_string)

输出: My name is David and I am 40 years old.

使用 F 前缀效果相同

F_string = F”My name is {name} and I am {age} years old.”
print(F_string)

输出: My name is David and I am 40 years old.

“`

关键特点:

  • 直接嵌入: 变量或表达式直接写在 {} 中,无需额外的方法调用或参数传递。
  • 运行时求值: {} 中的表达式在 f-string 被创建时(运行时)进行求值。
  • 极高可读性: 变量和它们在最终字符串中的位置紧密关联,一目了然。

2. 嵌入任意 Python 表达式

f-string 的强大之处在于 {} 内部几乎可以放入任何有效的 Python 表达式。

a. 变量

如上例所示,最常见的用法是直接嵌入变量。

b. 算术运算

“`python
x = 10
y = 5
result_string = f”The sum of {x} and {y} is {x + y}, the product is {x * y}.”
print(result_string)

输出: The sum of 10 and 5 is 15, the product is 50.

“`

c. 函数和方法调用

“`python
name = “evelyn”
names = [“Alice”, “Bob”, “Charlie”]

info_string = f”Name in uppercase: {name.upper()}. Number of names: {len(names)}.”
print(info_string)

输出: Name in uppercase: EVELYN. Number of names: 3.

def get_greeting(hour):
if 6 <= hour < 12:
return “Good morning”
elif 12 <= hour < 18:
return “Good afternoon”
else:
return “Good evening”

current_hour = 14
greeting_string = f”At {current_hour} o’clock, we say: {get_greeting(current_hour)}!”
print(greeting_string)

输出: At 14 o’clock, we say: Good afternoon!

“`

d. 访问对象属性和字典/列表元素

“`python
class Person:
def init(self, name, age):
self.name = name
self.age = age

person = Person(“Frank”, 55)
person_string = f”Person’s name: {person.name}, Age: {person.age}.”
print(person_string)

输出: Person’s name: Frank, Age: 55.

my_dict = {“item”: “laptop”, “price”: 1200}
my_list = [10, 20, 30]

data_string = f”Item: {my_dict[‘item’]}, Price: ${my_dict[‘price’]}. First element: {my_list[0]}.”
print(data_string)

输出: Item: laptop, Price: $1200. First element: 10.

“`

注意引号的使用: 如果在 {} 内需要访问字典的字符串键,确保内部使用的引号与 f-string 本身使用的引号不同。

“`python

正确示例

f”Item: {my_dict[‘item’]}” # f-string用双引号,内部用单引号
f’Item: {my_dict[“item”]}’ # f-string用单引号,内部用双引号

也可以使用三引号

f”””Item: {my_dict[“item”]} or {my_dict[‘item’]}”””

错误示例 (会导致 SyntaxError)

f”Item: {my_dict[“item”]}” # 内外引号相同

“`

三、 精通 f-string:格式说明符迷你语言

f-string 不仅仅是简单的表达式嵌入,它完全继承并兼容了 str.format()格式说明符迷你语言(Format Specification Mini-Language)。这使得我们可以对嵌入表达式的结果进行精细的格式控制。

格式说明符紧跟在表达式后面,用冒号 : 分隔。基本语法是:

f"{expression:format_specifier}"

下面详细介绍常用的格式说明符:

1. 对齐 (Alignment)

  • < : 左对齐 (默认)
  • > : 右对齐
  • ^ : 居中对齐
  • = : (仅对数字有效) 将符号放在填充字符的最左边,数字本身右对齐。

对齐通常需要配合 宽度 (Width) 使用。

“`python
text = “Hello”

左对齐,宽度 10

print(f”‘{text:<10}'”) # 输出: ‘Hello ‘

右对齐,宽度 10

print(f”‘{text:>10}'”) # 输出: ‘ Hello’

居中对齐,宽度 10

print(f”‘{text:^10}'”) # 输出: ‘ Hello ‘

number = -123.45

= 对齐,宽度 15,用 0 填充

print(f”‘{number:=+015.2f}'”) # 输出: ‘-0000000123.45’ (符号在最左,数字右对齐,0填充)
“`

2. 填充字符 (Fill Character)

可以指定一个字符用于填充对齐产生的空白,该字符写在对齐符号之前。

“`python
text = “World”

使用 ‘*’ 填充,居中对齐,宽度 15

print(f”‘{text:^15}'”) # 输出: ‘*World**’

使用 ‘_’ 填充,右对齐,宽度 10

print(f”‘{text:_>10}'”) # 输出: ‘_____World’
“`

3. 符号处理 (Sign)

  • + : 正数和负数都显示符号。
  • - : 仅负数显示符号 (默认)。
  • (空格) : 正数前留一个空格,负数显示符号。

“`python
positive = 42
negative = -42
zero = 0

print(f”Default: {positive}, {negative}, {zero}”) # 输出: Default: 42, -42, 0
print(f”Plus: {positive:+}, {negative:+}, {zero:+}”) # 输出: Plus: +42, -42, +0
print(f”Minus: {positive:-}, {negative:-}, {zero:-}”) # 输出: Minus: 42, -42, 0 (默认行为)
print(f”Space: {positive: }, {negative: }, {zero: }”) # 输出: Space: 42, -42, 0
“`

4. 宽度 (Width)

指定输出的最小宽度。如果值的长度小于宽度,则根据对齐方式进行填充。

python
num = 123
print(f"'{num:8}'") # 输出: ' 123' (默认右对齐数字)
print(f"'{num:<8}'") # 输出: '123 ' (左对齐)
print(f"'{num:08}'") # 输出: '00000123' (用0填充,默认右对齐)

5. 精度 (Precision)

  • 对于浮点数 (f, F, e, E, g, G, %): 指定小数点后的位数。
  • 对于非数字类型 (如字符串): 指定最大输出宽度,超出部分会被截断。

“`python
pi = 3.1415926535

浮点数精度

print(f”Pi (3 decimal places): {pi:.3f}”) # 输出: Pi (3 decimal places): 3.142 (会四舍五入)
print(f”Pi (2 significant digits, general format): {pi:.2g}”) # 输出: Pi (2 significant digits, general format): 3.1
print(f”Pi (scientific notation, 4 decimal places): {pi:.4e}”) # 输出: Pi (scientific notation, 4 decimal places): 3.1416e+00

字符串精度 (截断)

long_text = “This is a very long string.”
print(f”Truncated text: ‘{long_text:.10}'”) # 输出: Truncated text: ‘This is a ‘
“`

6. 千位分隔符 (Thousands Separator)

  • , : 使用逗号作为千位分隔符。
  • _ : 使用下划线作为千位分隔符 (常用于代码可读性,Python 3.6+)。

“`python
large_number = 1234567890
print(f”Comma separated: {large_number:,}”) # 输出: Comma separated: 1,234,567,890
print(f”Underscore separated: {large_number:_}”) # 输出: Underscore separated: 1_234_567_890

float_number = 12345.6789
print(f”Float with comma: {float_number:,.2f}”) # 输出: Float with comma: 12,345.68
“`

7. 类型表示 (Type Presentation)

指定如何表示不同类型的数据。

  • s : 字符串 (默认)。
  • d : 十进制整数。
  • f, F : 定点表示法 (默认 6 位小数精度)。
  • e, E : 科学计数法 (默认 6 位小数精度,e 小写,E 大写)。
  • g, G : 通用格式。对于给定精度 p >= 1,它会舍入到 p 位有效数字,然后根据数值大小以定点或科学计数法格式化。通常是最佳选择。
  • % : 百分比表示法 (将数值乘以 100 并显示 %,默认 6 位小数精度)。
  • b : 二进制表示法。
  • o : 八进制表示法。
  • x, X : 十六进制表示法 (x 小写,X 大写)。
  • c : 字符 (将整数转换为对应的 Unicode 字符)。

“`python
integer = 255
number = 0.75

print(f”String: {integer:s}”) # 输出: String: 255
print(f”Decimal: {integer:d}”) # 输出: Decimal: 255
print(f”Binary: {integer:b}”) # 输出: Binary: 11111111
print(f”Octal: {integer:o}”) # 输出: Octal: 377
print(f”Hex (lower): {integer:x}”) # 输出: Hex (lower): ff
print(f”Hex (upper): {integer:X}”) # 输出: Hex (upper): FF

print(f”Float (fixed): {number:f}”) # 输出: Float (fixed): 0.750000
print(f”Percentage: {number:%}”) # 输出: Percentage: 75.000000%
print(f”Percentage (1 decimal): {number:.1%}”) # 输出: Percentage (1 decimal): 75.0%

char_code = 65
print(f”Character: {char_code:c}”) # 输出: Character: A
“`

8. 组合使用

格式说明符可以组合使用,顺序通常是: [[fill]align][sign][#][0][width][,/_][.precision][type] ( # 用于为二进制、八进制、十六进制添加前缀 0b, 0o, 0x/0X0fill='0', align='=' 的简写)。

“`python
value = 1234.5678

组合示例:填充 ‘_’, 居中 ‘^’, 宽度 20, 千位分隔符 ‘,’, 精度 2f

print(f”‘{value:_^20,.2f}'”)

输出: ‘_1,234.57_____’

hex_value = 255

组合示例:带 0x 前缀 ‘#’, 宽度 8, 0 填充

print(f”‘{hex_value:#08x}'”)

输出: ‘0x0000ff’

“`

四、 f-string 的高级特性与技巧

1. 调试好帮手:= 说明符 (Python 3.8+)

Python 3.8 引入了一个非常实用的特性:在 f-string 的表达式后加上 =,它会自动输出表达式本身及其求值结果,非常适合快速调试。

“`python
x = 10
y = 20
user = “admin”

print(f”{x=}”) # 输出: x=10
print(f”{y = }”) # 输出: y = 20 (等号两边可以有空格)
print(f”{x + y = }”) # 输出: x + y = 30
print(f”User info: {user=}, calculation: {(x * y)=}”) # 输出: User info: user=’admin’, calculation: (x * y)=200

配合格式说明符

print(f”{x = :.2f}”) # 输出: x = 10.00
print(f”{user = !r}”) # 输出: user = ‘admin’
“`

这个小小的 = 极大地简化了打印变量值进行调试的过程。

2. 原始 f-string (Raw f-strings)

如果你需要在字符串中处理包含大量反斜杠 \ 的内容(例如 Windows 路径、正则表达式),并且同时想使用 f-string 的格式化功能,可以使用 rffr 前缀来创建 原始 f-string

在原始字符串中,反斜杠 \ 不会被特殊处理(除非它出现在引号前)。

“`python
path_segment = “user\docs”
filename = “report.txt”

普通 f-string,需要转义反斜杠

full_path_escaped = f”C:\path\to\{path_segment}\{filename}”
print(full_path_escaped) # 输出: C:\path\to\user\docs\report.txt

原始 f-string,反斜杠按字面处理

full_path_raw = rf”C:\path\to{path_segment}{filename}”
print(full_path_raw) # 输出: C:\path\to\user\docs\report.txt

注意:花括号内的表达式仍然会被求值

print(rf”Length of segment: {len(path_segment)}”) # 输出: Length of segment: 9
“`

限制: 在原始 f-string 的 {} 表达式内部,不能直接使用反斜杠。例如 rf"Newlines: { 'a\nb' }" 是无效的。

3. 多行 f-string

f-string 可以与 Python 的三引号("""''')结合使用,轻松创建包含变量和表达式的多行字符串。

“`python
name = “Alice”
age = 30
items = [“apple”, “banana”, “cherry”]

multiline_message = f”””
User Profile:


Name: {name.capitalize()}
Age: {age} years old
Items purchased:
“””
for i, item in enumerate(items):
multiline_message += f”- Item {i+1}: {item.capitalize()}\n”

multiline_message += f”””

Total items: {len(items)}
“””

print(multiline_message)
“`

输出:
“`
User Profile:


Name: Alice
Age: 30 years old
Items purchased:
– Item 1: Apple
– Item 2: Banana
– Item 3: Cherry


Total items: 3
“`

4. 花括号 {} 的转义

如果你需要在 f-string 的最终结果中包含花括号本身,可以通过双写花括号 {{}} 来实现转义。

“`python
value = 42
print(f”The value is {value}, which is represented inside {{curly braces}}.”)

输出: The value is 42, which is represented inside {curly braces}.

嵌套字典的示例,需要转义

json_like = f'{{ “name”: “{name}”, “value”: {value} }}’
print(json_like)

输出: { “name”: “David”, “value”: 40 } (假设 name=”David”, age=40)

“`

五、 f-string 的优势总结

相较于之前的格式化方法,f-string 提供了显著的优势:

  1. 可读性 (Readability): 表达式直接嵌入字符串中,代码更直观,易于理解变量在最终输出中的位置和上下文。
  2. 简洁性 (Conciseness): 语法更紧凑,减少了模板字符串和 .format() 调用之间的分离感,也减少了代码量。
  3. 性能 (Performance): f-string 通常比 str.format()% 操作符更快。因为 f-string 在编译时会解析成高效的字节码指令,直接进行字符串拼接和格式化,而 str.format() 需要在运行时进行更多的解析和方法调用。对于需要大量字符串格式化的场景,性能提升可能比较明显。
  4. 表达力 (Expressiveness): {} 内可以包含任意复杂的 Python 表达式,包括函数调用、算术运算、对象访问等,使得格式化逻辑可以直接写在字符串内部。
  5. 错误检查: 由于表达式在运行时直接求值,如果表达式有误(如变量名错误),会立即在 f-string 所在行抛出异常,便于定位问题。

六、 注意事项与潜在陷阱

虽然 f-string 非常强大和方便,但在使用时也需要注意一些细节:

  1. 引号嵌套: 如前所述,注意 f-string 本身的引号和其内部表达式中字符串引号的区别。
  2. 反斜杠处理:{} 内部的表达式中,反斜杠 \ 仍然具有其通常の转义含义。如果需要在表达式结果中包含字面反斜杠,可能需要双写 \\,或者在表达式外使用原始 f-string (rf)。
  3. Lambda 表达式限制: f-string 的 {} 内部不能直接包含 \ 字符(除非是字符串字面量内的转义),这限制了某些直接在 f-string 中定义包含反斜杠的 lambda 函数的写法(虽然这种场景很少见)。
  4. 表达式副作用: {} 中的表达式会被立即执行。如果表达式有副作用(例如修改全局变量、调用有状态的函数),需要注意其执行时机和可能带来的影响。
  5. 花括号转义: 牢记使用 {{}} 来输出字面花括号。

七、 结论:拥抱 f-string,提升开发体验

自 Python 3.6 引入以来,f-string 已经迅速成为 Python 社区进行字符串格式化的首选方式。它集简洁、可读、高效于一身,极大地改善了开发体验。无论是简单的变量替换,还是复杂的格式控制与表达式计算,f-string 都能以一种优雅且 Pythonic 的方式轻松应对。

对于还在使用旧版本 Python 或习惯于旧格式化方法的开发者,强烈建议开始学习并使用 f-string。掌握它不仅能让你的代码更加现代化、更易于维护,还能在某些情况下带来性能上的收益。从基础的变量嵌入到复杂的格式说明符,再到 Python 3.8+ 的 = 调试特性,f-string 提供了一个完整而强大的工具集。

总之,f-string 是现代 Python 开发中不可或缺的一部分。花时间深入理解并熟练运用它,无疑会让你在 Python 编程的道路上更加得心应手。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部