使用 !s 与 :s 格式化 Python 中的字符串

Using !s vs. :s to format a string in Python

我很好奇 Python 中的 :s 格式字符串 3. The documentation!sconversion 并且:sformat_spec.

它还说 !s 将应用 str(),但它没有说任何关于 :s 的类似内容。我认为它们之间没有显着差异,但我想确定一下。谁能澄清这些?

一些代码示例:

print("{!s}".format("this"))
print("{:s}".format("that"))
# I want to be sure that these two are processed identically internally

它仍然令人困惑,但让我用我自己(外行)的话来结束。

  1. type("whatever".format) 总是 str.
  2. 如果要在格式化之前将对象转换为 str,请使用 !s
  3. :s 表示对象(或转换后的对象)在某些内部格式化过程中将被视为 str。这是默认值 format_spec.

这里有什么问题吗?

根据文档,Python String Formatting:

The field_name is optionally followed by a conversion field, which is preceded by an exclamation point '!', and a format_spec, which is preceded by a colon ':'.

所以不,这不一样。

例如:

如果要将浮点数打印为字符串,则需要进行转换(浮点数→字符串)。

"{!s}".format(2.1457)
>>> 2.1457
type("{!s}".format(2.1457))
>>> <type 'str'>

如果不使用转换标记,会报错。

"{:s}".format(2.1457)
>>> ValueError: Unknown format code 's' for object of type 'float'

!s 及其兄弟 !a!r 分别在 之前应用 str()ascii()repr() 插值和格式化。这些称为 转换标志 ,并且是 Format String Syntax spec, not the per-field formatting spec 在插值时应用于值的一部分:

The conversion field causes a type coercion before formatting. Normally, the job of formatting a value is done by the __format__() method of the value itself. However, in some cases it is desirable to force a type to be formatted as a string, overriding its own definition of formatting. By converting the value to a string before calling __format__(), the normal formatting logic is bypassed.

大胆强调我的。

:s 仅适用于转换结果(或原始对象,如果未应用转换),并且仅当对象类型的 __format__ 方法支持该格式化选项时。通常,只有 str 类型的对象支持此格式化程序;它作为默认值存在,主要是因为 Format Specification Mini-Language allows for the existence of a type character and because the older % printf-style formatting 具有 %s 格式。如果您尝试将 s 类型应用于不支持它的对象,则会出现异常。

当您的对象本身不是字符串并且不支持格式化时(并非所有类型都支持),请使用 !s(或 !a!r)或格式不同于他们的 str()ascii()repr() 转换:

>>> class Foo:
...     def __str__(self):
...         return "Foo as a string"
...     def __repr__(self):
...         return "<Foo as repr, with åéæ some non-ASCII>"
...     def __format__(self, spec):
...         return "Foo formatted to {!r} spec".format(spec)
...
>>> print("""\
... Different conversions applied:
... !s: {0!s:>60s}
... !r: {0!r:>60s}
... !a: {0!a:>60s}
... No conversions: {0:>50s}
... """.format(Foo()))
Different conversions applied:
!s:                                    Foo as a string
!r:             <Foo as repr, with åéæ some non-ASCII>
!a:    <Foo as repr, with \xe5\xe9\xe6 some non-ASCII>
No conversions: Foo formatted to '>50s' spec

注意:所有格式规范指定的格式由__format__方法负责;最后一行不应用 >50s 格式规范中的对齐操作,Foo.__format__ 方法仅将其用作格式操作中的文字文本(此处使用 !r 转换)。

另一方面,对于转换后的值,使用 str.__format__ 方法,输出在 50 个字符宽的字段中右对齐,左侧用空格填充。

你真不走运,你使用字符串作为要格式化的值。使用几乎所有其他对象,您会发现它们有何不同。

通俗地说(尽我所能):

  • 转换标志的存在与否指定了我们要格式化的值的类型,并且在某种程度上,__format__ 我们将调用谁。正如 Martjin 指出的那样,通过使用它我们可以绕过某些行为并更通用地处理值(如字符串)。它具有三种不同的风格,对应于对象可以选择将自身表示为字符串的三种不同方式。
  • 类型说明符与其他说明符一起指定我们拥有的类型最终应该如何呈现。对于字符串,没有一组丰富的选项(字符串按原样显示),但是对于像 ints 这样的类型,您可以有不同的显示方式。

不过,我确实认为 type 可能是一个容易混淆的名称。