Python的字符串格式化有多种方式——旧方法是否已经被弃用?

117

Python至少有六种格式化字符串的方法:

In [1]: world = "Earth"

# method 1a
In [2]: "Hello, %s" % world
Out[2]: 'Hello, Earth'

# method 1b
In [3]: "Hello, %(planet)s" % {"planet": world}
Out[3]: 'Hello, Earth'

# method 2a
In [4]: "Hello, {0}".format(world)
Out[4]: 'Hello, Earth'

# method 2b
In [5]: "Hello, {planet}".format(planet=world)
Out[5]: 'Hello, Earth'

# method 2c
In [6]: f"Hello, {world}"
Out[6]: 'Hello, Earth'

In [7]: from string import Template

# method 3
In [8]: Template("Hello, $planet").substitute(planet=world)
Out[8]: 'Hello, Earth'

不同方法的简要历史:

  • printf-样式的格式化自Python诞生以来就存在了
  • Template类在Python 2.4中引入
  • format方法在Python 2.6中引入
  • f-字符串在Python 3.6中引入

我的问题是:

  • printf-样式格式化是否已被弃用或将被弃用?
  • Template class中,substitute方法是否已被弃用或将被弃用?(我指的不是safe_substitute,据我所知它提供了独特的功能)

类似的问题以及我为什么认为它们不是重复的:

另请参阅


1
我需要指出你忘记了Formatter类吗? - Martijn Pieters
5个回答

61
新的.format()方法旨在替换旧的%格式化语法。后者已经被淡化,但尚未正式弃用(但是)。该方法的文档如此说明:
“这种字符串格式化方法是Python 3中的新标准,并且应该优先考虑在新代码中使用,而不是在String Formatting Operations中描述的%格式化。”
(强调我自己)。
为了保持向后兼容性并使转换更加容易,旧格式仍然存在(目前)。来自原始PEP 3101 proposal的内容:

向后兼容性

保持向后兼容性的方法是保留现有机制。新系统不会与现有字符串格式化技术的任何方法名称冲突,因此两个系统可以共存,直到时候废弃旧系统。

请注意直到废弃旧系统为止;它还没有被废弃,但在编写新代码时应使用新系统。

新系统具有一个优点,即您可以结合旧的%格式化程序的元组和字典方法:

"{greeting}, {0}".format(world, greeting='Hello')

并且可以通过使用object.__format__()钩子来扩展处理单个值的格式化。

请注意,旧系统具有%Template类,后者允许您创建添加或更改其行为的子类。新式系统具有Formatter来填补相同的空缺。

Python 3进一步远离了弃用,而是在printf-style String Formatting部分中向您发出警告:

注意:这里描述的格式化操作显示出各种怪癖,导致许多常见错误(例如无法正确显示元组和字典)。使用较新的formatted string literalsstr.format()接口可以避免这些错误。这些替代方法还提供了更强大、灵活和可扩展的文本格式化方法。

Python 3.6还新增了格式化字符串字面量, 它可以将表达式直接嵌入到格式化字符串中。这是创建带有插值值的字符串最快的方法,应该在可以使用字面量的地方使用它,而不是str.format()

4
使用Formatter可以创建自定义格式,例如datetime对象使用的格式。另外,由于.format是一个函数,您可以直接使用它创建可调用的延迟格式化:例如,fmt = '{} - {}'.format; fmt(a, b) - Jon Clements
2
应优先选择使用str.format()而非%格式化字符串。这部分内容已从文档中删除。https://docs.python.org/3/library/stdtypes.html#str.format - AXO
1
我认为这个答案目前是误导性的;引用的第一段已经从Python 3文档中删除,而且对我来说似乎很清楚,没有任何剩余意图进行弃用。这个答案仍然具有历史价值,但我倾向于调整措辞,避免任何暗示仍然存在弃用的可能,并将答案的前半部分大部分编辑成过去式。如果你不反对,我会在某个时候自己这样做,但我想先发表评论,给你一个机会,如果你愿意的话,可以自己进行这些更改。 - Mark Amery
@MarkAmery 我不同意。这段内容在Python 2文档中有提到。Python 2文档并非静态的,它仍然使用相同的措辞是很重要的,并且str.format()现在是默认的格式化方法。请注意,PEP仍然使用相同的语言,未经修改。Python 3文档中的文本已经消失,因为旧式的%文档已经进行了改进并移动以减弱其作用。 - Martijn Pieters
1
@MartijnPieters 好的,我对自己对这个答案的批评持有立场;我认为你错误地解读了 Python 2 文档中留下的任何文字,而且我相信绝对没有废弃的可能性,这是我在 我的回答 中阐述的原因和其他回答者在此处提供的原因,但我不会编辑来根本改变你的答案的意义以使其与你不同意。 - Mark Amery
显示剩余4条评论

49

字符串格式化的%操作符并未被弃用,也不会被移除-尽管其他答案可能有所不同。
每次在Python开发列表上提到这个主题时,都会有很激烈的争议,但没有人争论是否要删除经典方式-它会一直存在。尽管在PEP 3101上进行了注明,Python 3.1已经过去,但%格式仍然存在。

保持经典样式的语句很清楚:它简单,快速,对于短小的事情快速完成。使用.format方法并不总是更可读 - 即使在核心开发人员中,几乎没有人能够在不查看参考资料的情况下使用.format提供的完整语法。即使在2009年,像这样的消息也已经出现:http://mail.python.org/pipermail/python-dev/2009-October/092529.html - 这个主题自那以后几乎没有再次出现在列表中。

2016更新

在当前Python开发版本(即将成为Python 3.6)中,有第三种字符串插值方法,描述在PEP-0498上。它定义了一个新的引号前缀“f""”(除了当前的u""b""r"")。

通过在字符串前面添加f,将在运行时调用字符串对象上的方法,该方法将自动将当前作用域中的变量插入到字符串中:

>>> value = 80
>>> f'The value is {value}.'
'The value is 80.'

3
让类型自己实现 __format__ 更好。例如,format(Decimal('0.1'), '.20f')'%.20f' % Decimal('0.1') 的区别。后者会将 Decimal 强制转换成浮点数。 - Eryk Sun
2
注意:我并没有争辩旧风格在所有方面都更好 - 只是它更短,有时更易读(有时不是)。当然,新方法更加灵活。 - jsbueno
Python 3 中是否有 f 的等效替代? - Daniel
上面使用的f-strings是Python 3.6中的新功能。在之前的版本中不存在,会引发语法错误。 - jsbueno
此外,在 f-strings 中,不仅可以插值变量,还可以插入任意表达式,可能包括任何任意函数和因此任何任意代码。 - Max

21

查看早期的Python文档和PEP 3101,有一份声明表明%运算符将来会被弃用并从语言中移除。在Python 3.0、3.1和3.2的Python文档中,以下声明是这样写的:

由于str.format()相当新颖,很多Python代码仍在使用%运算符。然而,因为这种旧式格式化方式最终将从语言中删除,通常应该使用str.format()。

如果您前往Python 3.3和3.4文档的同一部分,您将会发现该声明已被删除。我也无法在任何其他文档中找到任何关于此运算符将被弃用或从语言中移除的声明。同时,需要注意的是PEP3101已经超过两年半没有进行修改了(2011年9月30日,星期五)。

更新

PEP461 向字节和字节数组添加%格式化已被接受,应该成为Python 3.5或3.6的一部分。这是%运算符仍然活跃的又一个迹象。


19

Guido在这方面的最新表态可以在这里看到:

Python 3.0 中的新功能

PEP 3101: 字符串格式化的新方法

一种用于内置字符串格式化操作的新系统取代了%字符串格式化运算符。(但是,%运算符仍然受支持; 它将在Python 3.1中被弃用,并在以后的某个时候从语言中删除。) 阅读PEP 3101获取完整信息。

PEP3101本身的最后修改日期是 (Fri, 30 Sep 2011),所以我认为它最近没有进展。


19

虽然文档中有各种迹象表明.format和f-string比%字符串更优秀,但没有计划废弃后者。

在提交Issue #14123: Explicitly mention that old style % string formatting has caveats but is not going away any time soon.中,受到问题Indicate that there are no current plans to deprecate printf-style formatting的启发,关于%格式的文档进行了编辑,包含以下短语:

由于新的字符串格式化语法更加灵活,并可自然地处理元组和字典,因此建议在新代码中使用。然而,目前没有废除printf式格式化的计划

(重点是我的。)

这个短语后来被移除,在提交 Close #4966: revamp the sequence docs in order to better explain the state of modern Python中。这可能看起来像是废弃%格式的计划又回来了,但深入研究问题追踪器后发现,意图正好相反。在问题追踪器上,提交者对该修改进行了描述如下:

  • 更改了描述printf样式格式化和str.format方法之间关系的散文(有意地删除了前者任何真正消失的危险的暗示——我们根本不可能认真考虑杀死它)

换句话说,我们已经连续两次改变了%-格式化文档,旨在明确强调它将不会被废弃,更不用说删除了。文档在不同种类字符串格式化的相对优点方面有自己的见解,但同时也清楚%-格式化不会被废弃或删除。

此外,最近对该段落的更改是在2017年3月进行的,将其从以下内容更改...

本文描述的格式操作存在各种怪癖,会导致许多常见错误(如未能正确显示元组和字典)。使用更新的格式化字符串文字、str.format接口或模板字符串可避免这些错误。这些替代方案还提供了更强大、灵活和可扩展的文本格式化方法。

注意,“helps avoid”被改为“may help avoid”,而明确推荐使用.format和f-strings的语言已被替换成了模棱两可的措辞,关于每种风格“提供其自身的权衡和利益”。也就是说,不仅官方已不再考虑正式弃用%格式化方式,而且当前文档还公开承认,%格式至少在某些方面有一些“优点”。

从所有这些中我会推断出,弃用或移除 % 格式化的运动不仅已经停滞不前,而且已经被彻底地击败并永久性地失败了。


2
添加了蓬松的语言变更是为了安抚Mercurial维护者(以及其他人),他们不希望看到Mercurial被大量使用的代码库所淘汰。现在,“不进行大规模代码修改”政策已经取消,他们的反对意见也逐渐消失了。从长远来看,维护两种形式对“%”没有任何利益,在某个时候printf语法将被删除。我们只是还不知道什么时候,所以这种语言是值得缓和的。 - Martijn Pieters
@MartijnPieters 很有趣。听起来你对这个决定有很多了解,而我却缺乏这方面的知识。就我所知,如果你能提供一个详细引用的答案,概述这些要点(无论是作为新答案还是对现有答案进行编辑),那将会有很大的价值。 - Mark Amery

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接