f-string:一种改进的Python格式语法

f-string是Python3.6提供的一种新的格式化字符串的方法。 与其他格式化方式相比,代码更具可读性,更简洁且不易出错,而且速度更快! 在本文中,您将了解如何以及为什么现在就开始使用f-string。在使用f-string之前,我们还是先了解下之前是如何处理格式化的。

Python中的“老式”字符串格式

在Python 3.6之前,有两种方法可以将Python表达式嵌入字符串字面量进行格式化:%-formatting和str.format()。您将看到如何使用它们以及它们的局限性。

选项1:%格式

这是Python格式化的元老方式,从一开始就存在于该语言中。你可以在Python文档中阅读更多内容。请记住,%-formatting不被python文档推荐,文档中包含以下注意事项:

“这里描述的格式化操作会表现出各种古怪,导致许多常见错误(例如未能正确显示元组和字典)。 使用较新的格式化字符串文字或str.format()接口有助于避免这些错误。 替代方案还提供了更强大,灵活和可扩展的文本格式化方法。” https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting

如何使用%-formatting

字符串对象有一个使用%操作符的内置操作,您可以使用该操作符来格式化字符串。下面是实际情况:

>>> name = "Eric" >>> "Hello, %s." % name 'Hello, Eric.'

为了插入多个变量,必须使用这些变量的元组。你应该这样做:

>>> name = "Eric" >>> age = 74 >>> "Hello, %s. You are %s." % (name, age) 'Hello, Eric. You are 74.'

为什么%-formatting不是很好

您刚才看到的代码还尚可阅读。然而,一旦开始使用几个参数和更长的字符串,代码将很快变得不太容易阅读。

>>> first_name = "Eric" >>> last_name = "Idle" >>> age = 74 >>> 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 74. You are a comedian. You were a member of Monty Python.'

所以这种格式并不是很好,因为它很冗长,而且会导致错误,比如不能正确地显示元组或字典。幸运的是,未来的日子会更加光明。

选项2:str.format()

Python 2.6中引入了字符格式化的新方法str.format()

如何使用str.format()

str.format()是对%-formatting的改进。它使用普通函数调用语法,并可通过转换为字符串对象的__format__()方法进行扩展。

对于str.format(),替换字段用花括号标记:

>>> "Hello, {}. You are {}.".format(name, age) 'Hello, Eric. You are 74.'

通过引用变量的索引,你可以以任何顺序引用变量:

>>> "Hello, {1}. You are {0}.".format(age, name) 'Hello, Eric. You are 74.'

但如果你插入了变量名,你就可以在括号之间传递对象,然后引用参数和方法:

>>> person = {'name': 'Eric', 'age': 74} >>> "Hello, {name}. You are {age}.".format(name=person['name'], age=person['age']) 'Hello, Eric. You are 74.'

你也可以使用**来处理字典,使代码更加简洁:

>>> person = {'name': 'Eric', 'age': 74} >>> "Hello, {name}. You are {age}.".format(**person) 'Hello, Eric. You are 74.'

与%-formatting相比,str.format()绝对是一个升级,但它并不是一帆风顺的。

为什么str.format()不好

使用str.format()的代码比使用%-formatting的代码更容易读懂,但是当你处理多个参数和较长的字符串时,str.format()仍然可能相当冗长。比如下面的代码:

>>> first_name = "Eric" >>> last_name = "Idle" >>> age = 74 >>> profession = "comedian" >>> affiliation = "Monty Python" >>> print(("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 74. You are a comedian. You were a member of Monty Python.'

如果在字典中有想要传递给.format()的变量,那么只需使用.format(**some_dict)对其进行解包,并在字符串中按键引用值,但必须有更好的方法来做到这一点。

f-Strings: Python中格式化字符串的一种新的改进方法

f-strings是字符串字面量,它以f开头,花括号包含的表达式将被它们的值所替换。表达式在运行时计算,然后使用__format__协议进行格式化。和往常一样,当你想了解更多的时候,Python文档是你的朋友。

以下是f-strings可以让你的生活更轻松的一些方法。

简单语法

语法类似于您使用str.format()时使用的语法,但更简洁。看看这是多么容易读懂:

>>> name = "Eric" >>> age = 74 >>> f"Heloo, {name}. You are {age}." 'Heloo, Eric. You are 74.'

也可以使用大写字母F:

>>> F"Heloo, {name}. You are {age}." 'Heloo, Eric. You are 74.'

任意的表达式

因为f-string是在运行时计算的,所以可以在其中放入任何和所有有效的Python表达式。这允许你做一些漂亮的事情。

你可以做一些非常简单的事情,像这样:

>>> f"{2*40}" '80'

但是你也可以调用函数。这里有一个例子:

>>> def to_lowercase(input): ...     return input.lower() ... >>> name = "Liu DeHua" >>> f"{to_lowercase(name)} is funny." 'liu dehua is funny.'

你也可以直接调用一个方法:

>>> f"{name.lower()} is funny." 'liu dehua is funny.'

您甚至可以使用由带有f-string的类创建的对象。假设你有下面的class:

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", "74") >>> f"{new_comedian}" 'Eric Idle is 74.'

__str__()__repr__()方法处理对象如何使用字符串表示,因此您需要确保在类定义中至少包含这些代码。如果你必须选择一个,使用__repr__(),因为它可以用来代替__str__()

str()返回的字符串是对象的非正式字符串表示形式,应该是可读的。repr()返回的字符串是正式的表示形式,应该是无二义性的。调用str()repr()比直接使用__str__()__repr__()更可取。

默认情况下,f-strings将使用__str__(),但如果你包含转换标志,你可以确保它们使用__repr__() !

>>> f"{new_comedian}" 'Eric Idle is 74.' >>> f"{new_comedian!r}" 'Eric Idle is 74. Surprise!'

多行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放在每一行的前面,那么它就是普通的字符串,不会有格式化效果,如果你想把字符串分散到多行,你也可以选择用\来转义返回:

>>> 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.'

但如果你使用""",会发生这样的情况:

>>> 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'

速度

f字符串中的f也可以代表“快”。f-string%-formattingstr.format()都要快。正如您已经看到的,f-string是在运行时计算的表达式,而不是常量值。以下是这些文档的摘录:

”F-strings提供了一种方法,使用最小的语法将表达式嵌入到字符串字面量中。应该注意的是,f-string实际上是在运行时计算的表达式,而不是常量值。在Python源代码中,f-string是一个前缀为f的文字字符串,它包含大括号中的表达式。表达式被它们的值代替。”

在运行时,花括号内的表达式在它自己的范围内计算,然后与f-string的字符串文字部分放在一起。然后返回结果字符串。

这里是一个速度的比较:

>>> timeit.timeit("""name = "Eric" ... age = 74 ... '%s is %s.' % (name, age)""", number = 100000) 0.019910944000002928
>>> timeit.timeit("""name = "Eric" ... age = 74 ... '{} is {}.'.format(name, age)""", number = 100000) 0.03233340299999554
>>> timeit.timeit("""name = "Eric" ... age = 74 ... f'{name} is {age}'""", number = 100000) 0.017086811000012858

如你所见,f-string速度最快。

然而,情况并非总是如此。当它们第一次被实现时,还是有一些速度问题,需要比str.format()更快。引入了一个特殊的BUILD_STRING操作码。

Python f-Strings:讨厌的细节

可以在表达式内使用各种类型的引号。只是要确保在f-string的外部使用的引号类型与在表达式中使用的引号类型不同。

这段代码可以工作:

>>> f"{'Eric Idle'}" 'Eric Idle'

下面这段代码也可以运行:

>>> f'{"Eric Idle"}' 'Eric Idle'

你也可以使用三引号:

>>> f"""Eric Idle""" 'Eric Idle'
>>> f'''Eric Idle''' 'Eric Idle'

如果你发现你需要在字符串的内部和外部使用相同类型的引号,那么你可以用\:

>>> f"The \"comedian\" is {name}, aged {age}." 'The "comedian" is Eric Idle, aged 74.'

字典

说到引号,在使用词典时要特别当心。如果您打算对字典中的键使用单引号,那么请记住确保对包含键的f-string使用双引号。

这将工作:

>>> comdian = {'name': 'Eric Idle', 'age': 74} >>> f"The comedian is {comdian['name']}, aged {comdian['age']}." 'The comedian is Eric Idle, aged 74.'

但下面的代码会导致语法错误:

>>> comdian = {'name': 'Eric Idle', 'age': 74} >>> f'The comedian is {comdian['name']}, aged {comdian['age']}.'   File "<stdin>", line 1     f'The comedian is {comdian['name']}, aged {comdian['age']}.'                                    ^ SyntaxError: invalid syntax

如果在字典键周围使用与在f-string外部相同类型的引号,那么第一个字典键开头的引号将被解释为字符串的结束。

括号

为了让大括号出现在字符串中,你必须使用双大括号:

>>> f"{{70 + 4}}" '{70 + 4}'

注意,使用三括号将导致在你的字符串中只有一个括号:

>>> f"{{{70 + 4}}}" '{74}'

反斜杠

如前所述,可以在f-string的字符串部分中使用反斜杠转义。但是,你不能在f-string的表达式部分使用反斜杠来转义:

>>> f"{\"Eric Idle\"}"   File "<stdin>", line 1 SyntaxError: f-string expression part cannot include a backslash

你可以通过预先计算表达式并在f-string中使用结果来解决这个问题:

>>> name = "Eric Idle" >>> f"{name}" 'Eric Idle'

内联注释

表达式不应该包含使用#符号的注释。你会得到一个语法错误:

>>> f"Eric is {2 * 37 #Oh my!}."   File "<stdin>", line 1     f"Eric is {2 * 37 #Oh my!}."                                 ^ SyntaxError: f-string expression part cannot include '#'

前进并使用格式化!

您仍然可以使用旧的格式化字符串的方法,但是有了f-strings,您现在有了一种更简洁、可读和方便的方法,既更快又不容易出错。根据《Zen of Python》,当你决定如何做某事时,“这里应该有一个——最好只有一个——明显的方法来做这件事。”虽然f-strings不是你格式化字符串的唯一可能的方式,但它们处于一个很好的位置,成为一种明显的方式来完成这项工作。

f-string:一种改进的Python格式语法

您可能还会对下面的文章感兴趣: