网站搜索

Python 的整洁字符串格式迷你语言


当您在 Python 代码中进行字符串插值时,通常需要格式化插值以满足某些格式要求。为此,Python 提供了所谓的格式迷你语言,它定义了格式说明符的语法。

也许您对使用字符串感到自在,但您想要更好地控制它们。熟练掌握格式迷你语言后,您将能够使用格式说明符来执行诸如将数字格式化为货币值、使用科学记数法、将值表示为百分比等操作。

在本教程中,您将:

  • 了解格式迷你语言语法
  • 对齐填充代码中的文本输出
  • 在输出中的数据类型之间转换
  • 动态提供格式字段
  • 以不同的方式格式化数值

为了充分利用本教程,您应该熟悉 Python 的字符串插值工具,例如 str.format() 方法和 f-strings。

使用字符串插值和替换字段

当您使用字符串时,通常需要将值和对象嵌入或插入到字符串中,以便可以动态构建新字符串。此任务通常称为字符串插值

在 Python 中,您会发现三种流行的工具可让您执行字符串插值:

  1. 模运算符 (%)
  2. str.format() 方法
  3. F 字符串文字

模运算符是一种老式工具。它是 Python 中第一个字符串插值工具。不幸的是,它没有提供很多字符串格式化功能。因此,在本教程中,您将重点关注 str.format() 方法和 f 字符串。

要将值动态插入字符串中,您需要称为替换字段的东西。在 str.format() 和 f 字符串中,大括号 ({}) 分隔替换字段。在这些大括号内,您可以放置变量、表达式或任何对象。当您运行代码时,Python 会将该字段替换为实际值。

任何未包含在大括号中的内容都被视为文字文本,Python 将其原封不动地复制到输出中。

在以下部分中,您将了解替换字段在 str.format() 和 f 字符串中的工作原理。

str.format() 方法

您可以使用 str.format() 将值插入到字符串中。此方法对字符串对象进行操作,您可以根据需要在其中插入替换字段。然后 Python 将 .format() 的参数插入到字符串中以动态构建最终字符串:

>>> "Hello, {}!".format("Pythonista")
'Hello, Pythonista!'

在此示例中,您有一个包含替换字段的字符串。然后,您使用单个参数调用 .format()。当您运行此代码时,该方法会将其参数插入替换字段并构建最终字符串。

您可以通过多种方式使用 .format()。在上面的示例中,替换字段为空。但是,您可以使用从零开始的索引来定义特定的插入顺序。您还可以使用命名字段:

>>> "Hello, {0}! Good {1}!".format("Pythonista", "morning")
'Hello, Pythonista! Good morning!'

>>> "Hello, {name}! Good {moment}!".format(
...    name="Pythonista", moment="morning"
... )
'Hello, Pythonista! Good morning!'

在第一个示例中,您使用整数索引来定义将每个参数插入替换字段的顺序。在第二个示例中,您使用显式参数名称将值插入到最终字符串中。

Python 文档使用以下巴科斯-诺尔形式 (BNF) 表示法来定义 .format() 方法的替换字段的语法:

replacement_field ::=  "{"
                            [field_name]
                            ["!" conversion]
                            [":" format_spec]
                       "}"

从这个 BNF 规则中,您可以得出结论:字段名称是可选的。之后,您可以使用感叹号 (!) 提供快速转换字段。该字段可以采用以下形式之一:

  • !s 对参数调用 str()
  • !r 对参数调用 repr()
  • !a 对参数调用 ascii()

如您所见,conversion 字段允许您对要插入字符串的值使用不同的字符串表示形式。

作为 conversion 字段如何工作的示例,假设您有以下 Person 类:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"I'm {self.name}, and I'm {self.age} years old."

    def __repr__(self):
        return f"{type(self).__name__}(name='{self.name}', age={self.age})"

在此类中,您有两个实例属性:.name.age。然后,您可以使用 .__str__().__repr__() 特殊方法分别为您的类提供用户友好和开发人员友好的字符串表示形式。

要了解 conversion 字段的工作原理,请继续运行以下代码:

>>> from person import Person

>>> jane = Person("Jane Doe", 25)

>>> "Hi! {!s}".format(jane)
"Hi! I'm Jane Doe, and I'm 25 years old."

>>> "An instance: {!r}".format(jane)
"An instance: Person(name='Jane Doe', age=25)"

在这些示例中,您将 !s!r 转换字段用于不同的目的。在第一个示例中,您会收到一条用户友好的消息。在第二个示例中,您会收到一条对开发人员友好的消息。

替换字段语法的最后部分也是可选的。它是一个必须以冒号 (:) 开头的字符串,并且可以以格式说明符继续。您稍后将了解格式说明符。现在,您将继续使用 f 字符串并了解它们如何实现替换字段。

F弦

您会发现格式化字符串文字f-string是一种非常流行的字符串插值和格式化工具。 F 字符串文字以 fF 开头。要将值插入到 f 字符串中,您需要替换字段。与 str.format() 一样,替换字段由一对大括号 ({}) 分隔。

f 字符串语法非常简洁,这就是 f 字符串如此流行的原因。您不需要调用方法或使用运算符。您只需要将目标对象嵌入到替换字段中即可:

>>> name = "Pythonista"
>>> moment = "morning"

>>> f"Hello, {name}! Good {moment}!"
'Hello, Pythonista! Good morning!'

在此示例中,您定义两个变量来提供要插入到字符串中的值。然后,您可以直接在替换字段中使用这些变量。这很酷,不是吗?

定义 f 字符串中替换字段语法的 BNF 规则与定义 str.format() 方法中替换字段语法的规则非常相似:

replacement_field ::=  "{"
                             f_expression
                             ["="]
                             ["!" conversion]
                             [":" format_spec]
                       "}"

f_expression 部分指的是要插入到字符串中的值或表达式。请注意,此字段是必需的。然后,您有一个可选的等号 (=),它启用自记录表达式。

接下来,您将看到熟悉的 conversion 字段,其工作方式与 str.format() 方法相同:

>>> from person import Person

>>> jane = Person("Jane Doe", 25)

>>> f"Hi! {jane!s}"
"Hi! I'm Jane Doe, and I'm 25 years old."

>>> f"An instance: {jane!r}"
"An instance: Person(name='Jane Doe', age=25)"

同样,使用 !s,您可以获得用户友好的字符串表示形式,而使用 !r,您可以获得开发人员友好的表示形式。

现在您已经了解了替换字段在 str.format() 和 f 字符串中的工作原理,您将了解如何格式化字符串中的嵌入值。此过程通常称为字符串格式化,并利用格式说明符和格式说明符迷你语言。

了解Python格式迷你语言

您可以在替换字段中使用格式说明符来格式化要插入到字符串中的对象。一般来说,空格式说明符产生的结果与您使用该值作为参数调用 str() 的结果相同。同时,非空格式规范通常会修改结果。

定义工作格式说明符的语法有点复杂。因此,该语法被认为是一种完整的迷你语言。这是定义它的 BNF 符号:

format_spec     ::=  [[fill]align][sign]["z"]["#"]["0"][width]
                     [grouping_option]["." precision][type]
fill            ::=  <any character>
align           ::=  "<" | ">" | "=" | "^"
sign            ::=  "+" | "-" | " "
width           ::=  digit+
grouping_option ::=  "_" | ","
precision       ::=  digit+
type            ::=  "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" |
                     "G" | "n" | "o" | "s" | "x" | "X" | "%"

第一个 BNF 规则定义了通用语法,而以下规则定义了每个独立字段。例如,fill 字段接受任何字符。 align 字段可以是以下字符之一:<>=^。其余规则也有其他可能的值。

阅读和理解 BNF 表示法可能具有挑战性。因此,在下面的部分中,您将了解上述所有字段在实践中如何工作,以及如何将它们组合起来创建合适的格式说明符。首先,您将从与输出对齐密切相关的字段开始。

对齐和填充输出

当您开始编写自己的格式说明符时,您会发现 fillwidthalign 字段密切相关。使用它们,您可以对齐最终字符串中的插值。

width 字段提供工作空间,它由给定数量的允许字符组成。 align 字段允许您在工作空间内定位插值。最后,fill 字段可让您用您喜欢的字符填充剩余空间。

要为fill提供值,您可以使用任何字符。 Python 将使用此字符来填充插值周围的空间。同样,要为 width 提供值,您可以使用整数。

align 字段需要一些解释,您可以在下表中找到:

<

将插值在可用空间内向左对齐。这是大多数对象的默认对齐方式。

>

将插值在可用空间内右对齐。这是数字的默认对齐方式。

^

将插值值与可用空间的中心对齐。

=

在数值中的符号之后、数字之前添加填充。当 0 紧接在字段宽度之前时,这是数字的默认设置。

这些对齐值允许您控制给定值在结果字符串中的显示方式。为了说明 fillwidthalign 字段在实践中的工作原理,请考虑以下示例:

>>> text = "Hello!"

>>> # width=30
>>> f"{text:30}"
'Hello!                        '

>>> # align="<" and width=30
>>> f"{text:<30}"
'Hello!                        '

>>> # align="^" and width=30
>>> f"{text:^30}"
'            Hello!            '

>>> # align=">" and width=30
>>> f"{text:>30}"
'                        Hello!'

>>> # fill="=", align="^" and width=30
>>> f"{text:=^30}"
'============Hello!============'

使用 width 字段,您可以定义替换字段的字符总数。在此示例中,您总共使用 30 个字符的宽度。以下三个示例使用不同的 align 值将单词 "Hello!"width 字段。

在最后一个示例中,您使用等号 (=) 填充插值两侧的空格。看起来怎么样?

请注意,您需要遵循指定的字段顺序才能使格式说明符发挥作用。正如语法定义的那样,您需要按顺序填充、对齐、宽度。如果违反命令,则会出现错误:

>>> f"{text:30>}"
Traceback (most recent call last):
    ...
ValueError: Unknown format code '>' for object of type 'str'

在这种情况下,您将 width 字段放置在 align 字段之前。此顺序会导致 Python 引发 ValueError 异常。

类型表示之间的转换

使用格式说明符格式化字符串时的一个常见要求是使用不同的类型表示形式显示对象。要表示字符串值,您可以使用 s 表示类型。因为这是默认的,所以您也可以省略它:

>>> name = "Pythonista"

>>> f"Hello, {name:s}!"
'Hello, Pythonista!'

>>> f"Hello, {name:}!"
'Hello, Pythonista!'

在此示例中,您使用 s 表示类型来显示字符串。请注意,如果您提供 s 表示类型,那么您的格式说明符将不接受其他类型:

>>> f"{42:s}"
Traceback (most recent call last):
    ...
ValueError: Unknown format code 's' for object of type 'int'

当您在格式说明符中设置 s 表示类型时,当您尝试传递不同类型的对象(例如中的整数值)时,您将收到 ValueError上面的例子。

当涉及数值时,您可能希望将整数值显示为十六进制值或二进制值。为了实现这一点,格式迷你语言为您提供了几种类型修饰符。用于表示整数值的可用类型修饰符如下,默认为十进制 (d):

b

类型:二进制。 描述:将数字转换为基数 2

c

类型: 字符。 描述:将数字转换为对应的Unicode字符

d

类型:十进制整数。 描述:将数字转换为基数 10

o

类型:八进制。 描述:将数字转换为基数 8

x / X

类型: 十六进制。 描述:将数字转换为基数 16,对 9 以上的数字使用小写或大写字母

n

类型:数字。 描述:d 工作方式相同,不同之处在于它使用当前区域设置插入适当的千位分隔符

None

类型:十进制整数。 描述:d工作方式相同

您可以使用所有这些类型修饰符使用不同的表示类型来转换整数值。请注意,您不应将上表中的 None 与 Python 的 None 对象混淆。在此上下文中,None 表示您可以省略类型说明符。

这是一个小例子:

>>> number = 42

>>> f"int: {number:d},  hex: {number:x},  oct: {number:o},  bin: {number:b}"
'int: 42,  hex: 2a,  oct: 52,  bin: 101010'

此 f 字符串使用不同的类型修饰符将整数转换为其他类型。请注意,类型修饰符会为您完成艰苦的工作,将值从一种表示形式转换为另一种表示形式。

您在上面看到的类型修饰符并不是 Python 格式迷你语言中唯一可用的类型修饰符。它还提供十进制类型的修饰符:

e or E

科学计数法,分隔符分别为小写或大写

f or F

定点表示法,naninf 分别为小写或大写

g or G

一般格式,其中较小的数字以定点表示法表示,较大的数字以科学计数法表示

n

通用格式(与 g 相同),不同之处在于它使用区域设置感知字符作为千位分隔符

您可以使用上述类型修饰符来使用不同的表示形式来表达浮点数。以下是这些修饰符在实践中如何工作的一些玩具示例:

>>> large = 1234567890
>>> f"{large:e}"
'1.234568e+09'
>>> f"{large:E}"
'1.234568E+09'

>>> number = 42.42
>>> f"{number:f}"
'42.420000'
>>> inf = float("inf")
>>> f"{inf:f}"
'inf'
>>> f"{inf:F}"
'INF'

>>> f"{large:g}"
'1.23457e+09'
>>> f"{large:G}"
'1.23457E+09'
>>> f"{number:g}"
'42.42'

在第一组示例中,您使用 eE 使用科学记数法表示大量数字。

接下来,您使用 fF 类型修饰符。这两者之间的区别在于 infnan 值将使用小写字母与 f 和大写字母与 F >。

最后,gG用科学计数法表示较大的数字,用定点计数法表示较小的数字。 gG 之间的区别在于,当您将它们与大数字一起使用时,科学计数法分隔符将分别为小写或大写。

设置数值格式以改进演示

格式说明符迷你语言有多个选项来格式化数值。您可以使用这些选项来调整在程序输出中显示数字的方式。您可以选择控制小数精度、添加千位分隔符、将数字表示为百分比以及向数值添加前导符号。

在以下部分中,您将了解所有这些选项以及如何将它们用作格式说明符的一部分。

设置小数精度

格式说明符的 BNF 语法中的精度字段接受一个整数值,指示 fF 的小数点后应有多少位数字演示文稿类型。该字段非常有用,因为它允许您以统一的方式显示浮点值。

在下面的示例中,您将使用不同的精度表示 pi 的值。要设置精度,您需要显式使用一个点,后跟精度值,然后是 f 或 F 类型:

>>> from math import pi

>>> pi
3.141592653589793

>>> f"{pi:.4f}"
'3.1416'

>>> f"{pi:.8f}"
'3.14159265'

在此示例中,您使用精度 4,然后使用 8 来显示 pi 的值。请注意,结果数字在显示时会自动舍入,这是在格式说明符中使用精度字段的另一个效果。

使用千位分隔符

格式说明符语法还提供了一个grouping_option字段来方便地对数值进行分组并提高可读性。此字段允许您使用特定的千位分隔符格式化数值。该字段可以采用两个值之一:

,

允许您使用逗号作为千位分隔符

_

允许您使用下划线作为千位分隔符

这两个值适用于定点表示类型和 d(十进制)整数表示类型。

要了解 grouping_option 字段的工作原理,请考虑以下示例:

>>> number = 1234567890

>>> f"{number:,}"
'1,234,567,890'

>>> f"{number:_}"
'1_234_567_890'

>>> f"{number:,.2f}"
'1,234,567,890.00'

>>> f"{number:_.2f}"
'1_234_567_890.00'

在前两个示例中,您使用逗号和下划线作为整数值中的千位分隔符。请注意,使用 d 整数表示类型您将获得相同的结果。

在最后两个示例中,您使用逗号和下划线对数千进行分组。请注意,在本例中,您将转换原始整数并将其表示为精度为 2 的定点数。

添加标志

格式说明符语法中的 sign 字段将允许您调整包含符号的数字的表示方式。该字段可以采用以下值之一:

+

表示正数和负数均应使用符号

-

指示符号只能用于负数,这是默认行为

space

表示正数应使用前导空格,负数应使用减号

使用这些值,您可以微调在显示数值时呈现符号的方式。以下是 sign 字段如何运作的简短示例:

>>> positive = 42
>>> negative = -42

>>> f"{positive:+}"
'+42'
>>> f"{negative:+}"
'-42'

>>> f"{positive:-}"
'42'
>>> f"{negative:-}"
'-42'

>>> f"{positive: }"
' 42'
>>> f"{negative: }"
'-42'

在前两个示例中,您使用加号 (+) 始终在数值前面显示一个符号。在接下来的两个示例中,减号 (-) 允许您显示不带符号的正数和带负号的负数。

最后,使用空格来显示带前导空格的正数和带相应负号的负数。

动态提供格式化字段

到目前为止,您已经学习了 Python 字符串格式化迷你语言的基础知识。在到目前为止您看到的所有示例中,您都使用字符串对格式说明符进行了硬编码。但是,有时您需要动态构建格式说明符。

为此,您可以使用大括号将变量和表达式嵌入到格式说明符中。考虑以下示例:

>>> total = 123456.99

>>> # Formatting values
>>> width = 30
>>> align = ">"
>>> fill = "."
>>> precision = 2
>>> sep = ","

>>> f"Total{total:{fill}{align}{width}{sep}.{precision}f}"
'Total....................123,456.99'

在此示例中,您使用大括号内的变量构建格式说明符。为了提高可读性,您可以使用与格式说明符语法中的名称基本相同的名称,但 sep 除外,它表示分组选项。

动态创建格式说明符的可能性是一个强大的工具,它允许您为字符串格式化代码添加灵活性。

格式化字符串:实例

现在您已经了解了格式说明符的基本语法并了解了如何动态生成它们,现在是时候深入研究一些何时要在字符串中使用格式说明符的实际示例了。

首先,您将从字符串格式的一个非常常见的用例开始:表示货币值。然后,您将探索格式说明符的其他常见用例。

代表货币价值

将数字格式化为货币值可能是代码中的常见要求。您可以精心设计格式说明符来满足此需求。例如,假设您想要创建一个报告,列出您的产品库存及其价格。

为此,您可以使用以下代码:

>>> inventory = [
...     {"product": "Apple", "price": 5.70},
...     {"product": "Orange", "price": 4.50},
...     {"product": "Banana", "price": 6.00},
...     {"product": "Mango", "price": 8.60},
...     {"product": "Pepper", "price": 4.20},
...     {"product": "Carrot", "price": 3.57},
... ]

>>> for item in inventory:
...     product = item["product"]
...     price = item["price"]
...     print(f"{product:.<30}${price:.2f}")
...
Apple.........................$5.70
Orange........................$4.50
Banana........................$6.00
Mango.........................$8.60
Pepper........................$4.20
Carrot........................$3.57

在此示例中,您首先定义一个包含水果和蔬菜库存的字典。在循环中,您迭代库存中的商品,获取产品的名称和价格,最后打印包含所需信息的格式化字符串。

在格式说明符中,使用点作为填充字符。然后,将产品名称向左对齐。最后,您使用 2 精度显示产品的价格。美元符号使该值看起来像货币值。

设置日期格式

字符串格式化的另一个常见用例是当您需要格式化日期和时间值时。如果您使用的是 datetime 模块,那么很高兴知道日期格式化代码可以与 f 字符串或 .format() 方法配合良好。因此,在处理日期时,您可以使用日期和时间格式代码来创建格式说明符:

>>> from datetime import datetime

>>> now = datetime.now()
>>> now
datetime.datetime(2024, 1, 31, 14, 6, 53, 251170)

>>> >>> f"Today is {now:%a %b %d, %Y} and it's {now:%H:%M} hours"
"Today is Wed Jan 31, 2024 and it's 14:06 hours"

在此示例中,您使用 datetime 中的 now() 来获取当前日期和时间。当然,当您运行此代码时,您将获得不同的日期和时间。然后,创建一个使用日期和时间格式代码作为格式说明符的 f 字符串。这些代码允许您创建格式良好的日期。

表达百分比

您可以使用格式说明符获得的另一种很酷的格式是百分比格式。 % 修饰符允许您将数字表示为百分比。在底层,此修饰符将数字乘以 100,以定点表示法显示,并在末尾添加百分号。

例如,假设您要计算一支篮球队的胜率。在这种情况下,您可以定义精度 2,然后包含 % 修饰符,如以下代码所示:

>>> wins = 25
>>> games = 35

>>> f"Team's winning percentage: {wins / games:.2%}"
"Team's winning percentage: 71.43%"

请注意 % 修饰符如何允许您将获胜次数除以游戏总数的结果表示为获胜百分比。请注意,精度不是必需的。您可以单独使用 % 修饰符,在这种情况下,您将获得与 Python 返回的十进制数字一样多的十进制数字。

重要的是不要将百分比符号 (%) 的使用与模运算符混淆。在格式说明符的上下文中,该符号具有不同的含义。

探索迷你语言的其他选项

至此,你已经了解了字符串格式迷你语言的主要特性。但还有更多有待发现!在本节中,您将了解更多在创建自己的格式说明符时可能会派上用场的选项。在实践中,您将了解两个附加修饰符:

  • z 强制使用负零。
  • # 添加类型表示前缀。

在 Python 3.11 中,格式说明符语法已扩展为包含 z。 z 修饰符在舍入到格式精度后将负零浮点值强制转换为正零。此选项仅适用于浮点表示类型。

是的,Python 有负零值:

>>> neg_zero = -0.0
>>> neg_zero
-0.0

在某些情况下这可能是一种奇怪的行为。因此,您可能需要删除负号。为此,您可以使用 z 选项:

>>> neg_zero = -0.0
>>> f"{neg_zero:z.4f}"
'0.0000'

在此示例中,您使用 z 选项将负零强制转换为正零。请注意,这仅适用于定点表示类型,fF。此外,z 选项会在输出中附加零以填充预期的精度。在示例中,您使用的精度为 4,因此小数点后有四个零。

您可以在格式说明符语法中找到的另一个有用选项是 # 修饰符。此选项允许您使用其替代形式来呈现数值。不同的数值数据类型有不同的替代形式。下表总结了 # 修饰符在每种数据类型中的作用:

int

将前缀 0b0o0x0X 添加到二进制、八进制、十六进制和大写十六进制符号分别

float and complex

将小数点字符添加到值的末尾,即使后面没有数字

正如您所看到的,替代形式修饰符 (#) 以不同的方式影响数字类型的表示。要了解此修饰符的工作原理,请考虑以下示例:

>>> number = 42

>>> f"hex: {number:#x};  oct: {number:#o};  bin: {number:#b}"
'hex: 0x2a;  oct: 0o52;  bin: 0b101010'

>>> f"float: {number:#.0f}"
'float: 42.'

在第一个示例中,您使用不同的整数变体来表示数字。 # 说明符向输出添加适当的前缀。

在第二个示例中,您将数字表示为浮点值。在本例中,精度为 0,这意味着您不想显示十进制值。但是,# 修饰符会附加小数点,因此任何人都可以知道这是一个浮点值。

结论

现在您知道如何创建格式说明符来格式化使用 Python 的格式迷你语言插入到字符串中的值。格式说明符是遵循此迷你语言中定义的语法的字符串。通过适当的格式说明符,您可以将数字格式化为货币值、使用科学记数法、将值表示为百分比等等。

在本教程中,您学习了如何:

  • 在字符串中使用格式迷你语言语法
  • Python 代码中对齐填充文本输出
  • 在输出中的数据类型之间转换
  • 使用变量和表达式动态提供格式化字段
  • 以不同的方式格式化数值以解决不同的问题

有了这些知识,您现在就可以开始在 Python 代码中创建格式良好的字符串和消息了。