f-string: 改进的 Python 字符串格式化语法
f-strings 是 Python 3.6 引入的一种非常棒的字符串格式化方法。
相比其他格式方法,f-strings 更易读、更简洁、更少犯错,也更快。
在了解为什么以及如何使用 f-strings 之前,我们先看一下在 f-strings 之前 Python 是如何进行字符串格式化的。那是一段很艰难的日子,犹如大雪中艰难攀爬上下学路上的山坡。
【Python 中字符串格式的老式方法】
Python 3.6 之前,有两种主要方式可将 Python 表达式嵌入到常量字符串中进行格式化:% 格式化语法和 str.format() 方法。
我们来看一下这两种方法的使用方法和局限之处。
1,% 格式化语法
这是 Python 中最古老的一种格式化方法,从一开始就存在于语言规范中。你可以在 Python 官方文档中获取更多相关信息。
你需要知道,官方文档并不推荐 % 格式化方法,并给出如下理由:
那么,如何使用 % 格式化语法呢?
string 类型的对象支持使用 % 运算符来执行字符串格式化操作。
例如:
>>> name = 'Eric'>>> 'Hello, %s' % name'Hello, Eric'如果要插入多个变量,你必须使用 tuple 来包含这些变量。
>>> name = 'Eric'>>> age = 21>>> 'Hello %s. You are %s.' % (name, age)'Hello Eric. You are 21.'上边这两段代码可读性还是不错的,但是,一旦你需要使用更多的变量和更长的格式字符串,代码的可读性就变得差很多了。
>>> first_name = 'Eric'>>> last_name = 'Idle'>>> age = 21>>> profession = 'comedian'>>> affiliation = 'Monty Python'>>> "Hello, %s %s. You are %s. You are a %s. You were a member of %s." % (first_name, last_name, age, profession, affiliation)'Hello, Eric Idle. You are 21. You are a comedian. You were a member of Monty Python.'这段代码开始显得冗长,而且容易产生错误:你可能在格式字符串中漏写一个 %,或者未能正确显示 tuple 或 dict 对象。
幸运的是,格式化之路前途光明。
2,str.format() 方法
这是 Python 2.6 引入的新式格式化方法,能胜任 % 完成的所有工作。
str.format() 是 % 格式化语法的一个改进。它采用了正常的函数调用语法,并且可通过待格式化对象的 __format__() 方法进行扩展。
str.format() 在格式字符串中使用大括号为待格式化对象预留替换位。
>>> "Hello, {}. You are {}.".format(name, age)'Hello, Eric. You are 21.'可以通过变量在 str.format() 参数中的位置索引来引用这些对象。
>>> "Hello, {1}. You are {0}.".format(age, name)'Hello, Eric. You are 21.'也可以在格式字符串中插入变量名,这样你就能够传递对象,并在格式字符串的大括号里引用参数和方法。
>>> person = {'name': 'Eric', 'age': 21}>>> "Hello, {name}. You are {age}.".format(name=person['name'], age=person['age'])'Hello, Eric. You are 21.'>>> "Hello, {name}. You are {age}.".format(age=person['age'], name=person['name'])'Hello, Eric. You are 21.'>>>>>> name = 'Eric'>>> age = 21>>> "Hello, {name1}. You are {age1}.".format(age1=age, name1=name)'Hello, Eric. You are 21.'对于字典(dict)类型的变量,也可以使用 ** 来实现这个操作:
>>> person = {'name': 'Eric', 'age': 21}>>> "Hello, {name}. You are {age}.".format(**person)str.format() 相比 % 格式化语法无疑是一个升级,但难称完美。
插入变量名使得它比 % 格式化语法更易读;可在参数中对字典类型对象执行解压操作,使得代码更简洁。但是处理多个参数或较长字符串时同样显得冗长。
>>> first_name = "Eric">>> last_name = "Idle">>> age = 21>>> profession = "comedian">>> affiliation = "Monty Python">>> "Hello, {first_name} {last_name}. You are {age}.You are a {profession}. You were a member of {affiliation}.".format(first_name=first_name, last_name=last_name, age=age, profession=profession, affiliation=affiliation)'Hello, Eric Idle. You are 21.You are a comedian. You were a member of Monty Python.'【f-strings:一种新的改进的字符串格式化方法】
f-strings 是 Python 3.6 引入的一种新式字符串格式化方法,使得字符串格式化工作更简单。
f-strings 也称为格式化的字符串字面量,它以 f 开头,在引号包含的字符串中使用大括号包含待求值的表达式。这些表达式在运行时求值,然后通过 __format__ 协议实现格式化。
一起看一下 f-strings 是如何简化格式化操作的吧。
1,简单语法
f-strings 的基本语法和 str.format() 相似,但是更简洁。
>>> name = 'Eric'>>> age = 21>>> f'hello, {name}. You are {age}.''hello, Eric. You are 21.'只需直接写一遍变量名即可。
也可以使用大写的 F 作为前缀:
>>> F"Hello, {name}. You are {age}."'Hello, Eric. You are 21.'2,支持各种表达式
由于 f-strings 是在运行时求值,你可以在其中放置任意合法的 Python 表达式,这让你可以做一些很巧妙的事情。
你可以做一些很直接的事情:
>>> f'{2 * 37}''74'也可以调用函数:
>>> def to_lowercase(input):... return input.lower()...>>> name = "Eric Idle">>> f"{to_lowercase(name)} is funny."'eric idle is funny.'还可以直接调用类方法:
>>> f"{name.lower()} is funny."'eric idle is funny.'你甚至可以在 f-strings 中格式化较复杂的对象,只要对象的类中恰当运用了 f-strings。
假设你有一个下边这样的类:
class Comedian: def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age
def __str__(self): return f"{self.first_name} {self.last_name} is {self.age}."
def __repr__(self): return f"{self.first_name} {self.last_name} is {self.age}. Surprise!"你可以这样直接格式化这个类的对象:
>>> new_comedian = Comedian("Eric", "Idle", "21")>>> f"{new_comedian}"'Eric Idle is 21.'__str__() 和 __repr__() 方法用于处理对象如何表示为字符串,你至少应该在类定义中包含其中一个方法。如果只定义一个的话,请使用 __repr__(),因为它可用来替代 __str__()。
__str__() 返回的字符串是一个对象的非正式字符串表示,应该是可读的。__repr__() 返回的字符串是正式的表示,应该是明确的。
调用 str() 和 repr() 方法比直接使用 __str__() 和 __repr__() 要好。
默认情况下,f-strings 会使用 __str__() 来输出对象的字符串表示,但是如果你加入了转换标志 !r,f-strings 就会调用 __repr__()。
>>> f"{new_comedian}"'Eric Idle is 21.'>>> f"{new_comedian!r}"'Eric Idle is 21. Surprise!'3,多行 f-strings
你可以在 f-strings 中使用多行字符串。
>>> name = "Eric">>> profession = "comedian">>> affiliation = "Monty Python">>> message = (... f"Hi {name}. "... f"You are a {profession}. "... f"You were in {affiliation}."... )>>> message'Hi Eric. You are a comedian. You were in Monty Python.'但是请注意,你需要在多行字符串的每一行之前放置 f 前缀。否则,格式化结果会出错:
>>> message = (... f"Hi {name}. "... "You are a {profession}. "... "You were in {affiliation}."... )>>> message'Hi Eric. You are a {profession}. You were in {affiliation}.'这段代码只有第一个变量名被替换为实际值。
你也可以使用转义符 \ 将 f-strings 扩展到多行。
>>> message = f"Hi {name}. " \... f"You are a {profession}. " \... f"You were in {affiliation}.">>>>>> message'Hi Eric. You are a comedian. You were in Monty Python.'如果使用三引号字符串来定义多行 f-strings,输出的格式化字符串也将是多行的。
>>> message = f"""... Hi {name}.... You are a {profession}.... You were in {affiliation}.... """>>> message'\n\tHi Eric. \n\tYou are a comedian. \n\tYou were in Monty Python.\n'4,运行速度
f-strings 中的 f 也可以代表 “fast”。通常情况下,f-strings 比 % 格式化语法和 str.format() 都要快。
如你所见,f-strings 实际上是一个运行时求值的表达式,而不是常量值。
运行时,大括号中的表达式先在其自己的作用域中求值,然后和 f-strings 的字符串常量部分组合在一起,并返回结果字符串。这就是 f-strings 的全部工作。
这里给出一个比较三种格式化方式速度的例子。
>>> import timeit>>> timeit.timeit("""name = "Eric"... age = 74... '%s is %s.' % (name, age)""", number = 10000)0.003324444866599663
>>> timeit.timeit("""name = "Eric"... age = 74... '{} is {}.'.format(name, age)""", number = 10000)0.004242089427570761>>> timeit.timeit("""name = "Eric"... age = 74... f'{name} is {age}.'""", number = 10000)0.0024820892040722242可以看出,f-strings 速度较快。
但结果并非总是如此,不同版本的 Python 中,str.format() 有时候会快一些。
【f-strings 的一些语法细节】
我们已经了解到 f-strings 拥有简洁的语法和较快的速度,在开始使用 f-strings之前,再来了解几点细节知识。
1,引号
在 f-strings 表达式中可以使用多种引号,但是 f-strings 内部表达式使用的引号和 f-strings 外部的引号不能相同。
下边这两种写法都是正确的:
>>> f"{'Eric Idle'}"'Eric Idle'>>> f'{"Eric Idle"}''Eric Idle'也可以使用三引号:
>>> f"""Eric Idle"""'Eric Idle'>>> f'''Eric Idle''''Eric Idle'如果你想在 f-strings 的内部和外部使用相同的引号,你需要使用转义符 \ 来转义内部引号:
>>> f"The \"comedian\" is {name}, aged {age}."'The "comedian" is Eric, aged 21.'2,字典变量
当你打算在 f-strings 中插入字典变量的某些 key 时,若你使用单引号引用 key 名,你需要在 f-strings 的外部使用双引号。
>>> comedian = {'name': 'Eric Idle', 'age': 21}>>> f"The comedian is {comedian['name']}, aged {comedian['age']}."'The comedian is Eric Idle, aged 21.'若你在 f-strings 的外部使用了和引用字典 key 相同的单引号,Python 解释器会报错:
>>> comedian = {'name': 'Eric Idle', 'age': 21}>>> f'The comedian is {comedian['name']}, aged {comedian['age']}.' File "<stdin>", line 1 f'The comedian is {comedian['name']}, aged {comedian['age']}.' ^SyntaxError: invalid syntax所以,在 f-strings 中插入字典的 key 时,需要特别留意引号的使用方法。
3,大括号
由于 f-strings 使用大括号来插入变量,如果想在 f-strings 中输出 {} 字符,你需要使用双重大括号。
>>> f"{{70 + 4}}"'{70 + 4}'但是,如果使用三重大括号,格式化结果中只会保留一重大括号:
>>> f"{{{70 + 4}}}"'{74}'如果使用超过三重的大括号,你会在输出中得到更多大括号。
>>> f"{{{{70 + 4}}}}"'{{70 + 4}}'4,反斜杠
>>> name = 'Eric'>>> f"The \"comedian\" is {name}."'The "comedian" is Eric.'
>>> f"{\"Eric\"}" File "<stdin>", line 1SyntaxError: f-string expression part cannot include a backslash>>> f"Eric is {2 * 37 #Oh my!}." File "<stdin>", line 1SyntaxError: f-string expression part cannot include '#'【结语】
f-strings 作为新式字符串格式化方法,在简洁性、可读性和运行速度上都优于其他方法,建议使用 Python 3.6 以上版本的小伙伴优先采用此方法来格式化字符串。
