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 1 SyntaxError: f-string expression part cannot include a backslash 5,内嵌评论 >>> f"Eric is {2 * 37 #Oh my!}." File "<stdin>", line 1 SyntaxError: f-string expression part cannot include '#' 【结语】 f-strings 作为新式字符串格式化方法,在简洁性、可读性和运行速度上都优于其他方法,建议使用 Python 3.6 以上版本的小伙伴优先采用此方法来格式化字符串。 |
|
来自: RealPython > 《python 技术》