对于一种语言,我们主张关注于用一种合适的方法来处理大部分的情况,Python字符串格式化却是一个另类,而且其越来越多样化。从 Python 3.6 开始我们有三种字符串格式化的方式(除了简单的连接或使用 string.Template):

  • 使用%操作符
  • str.format
  • 插入字符串

(如果你不想主动阅读所有的这些,我会在PyGraz meetup in February 2016里给你一个关于这个稍微扩展的闪电式演讲,里面会有更多一点的例子)

%-formatting

%-formatting从1.0版本开始就成为语言的一部分了。如果你用过Python3之前的版本,你会知道这个。

1 \”%s %s\” % (\’Hello\’, \’World\’,)

这个或多或少的有点像C语言的 sprintf 。它会起作用,但是使用起来有点复杂。

因为它只支持有限的类型,所以在传值到字符串格式化器之前,你得把你的自定义对象转换成它支持的类型之一。

很多年后,本地字符串数据类型扩展了一种 format 方法:

str.format()

在2008年10月被添加进Python2.6,类似于上下文管理器。在PEP-3101有详细描述,它着力于解决老的二元操作符%的一些不足,比如只支持有限的类型,以及在实际处理整个表达式右半部分的时候,会出现一些容易导致错误特殊的情况。

1234 >>> \”%s\” % (\”lala\”,)\’lala\’>>> \”%s\” % \”lala\”\’lala\’

由于 .format 是一个方法而不是操作符(被映射到一个二元函数),处理参数会更加明确。如果你传入一个字符串,它就会被解释为一个字符串。如果你传入只包含一个字符串的元组,那它就被解释为只包含一个字符串的元组。

1234 >>> \”{}\”.format(\”lala\”)\’lala\’>>> \”{}\”.format((\”lala\”,))\”(\’lala\’,)\”

对比与%操作符,它还支持使用命名参数而不需要字典。

1 \”{firstname} {lastname}\”.format(firstname=\”Horst\”, lastname=\”Gutmann\”)

起初,它的发明是为了完全取代%操作符(它被计划用来反对Python 3.1的老式格式化功能)但是并没有完全发生。这个字符串格式化器的核心功能基本是和老的%操作符一样的,但语法略有不同,恕我直言更为直观一些。实际上,因为Ulrich和我创建了pyformat.info来帮助人们迁移到新系统。

但是,很显然,pep – 3101并没有停留在只是清理旧的特性。它还引入了一个协议,允许使用有更多样化交互的自定义类:

12345678910111213 class Country:    def __init__(self, name, iso):        self.name, self.iso = name, iso     def __format__(self, spec):        if spec == \’short\’:            return self.iso        return self.name country = Country(\”Austria\”, \”AUT\”) print(\”{}\”.format(country))print(\”{:short}\”.format(country))

你可以想象对于字符串格式化 __format__ 方法像 __str__ 一样你可以传递选择项。如果你在你的对象中有 __format__ 方法,当你使用格式化方法时它就会代替 __str__ (除非你做些什么比如 \"{!s}\".format(country)\")。

事实上你会在 datetime.date class in Python 3.4找到一个关于如何使用的很好的例子:

123456 class date:    ...    def __format__(self, fmt):        if len(fmt) != 0:            return self.strftime(fmt)        return str(self)

这允许你使用“parent”字符串格式直接来格式化日期,这样就不需要首先把你的日期对象转换成一个字符串,然后再传递到字符串格式化器中:

123 import datetimeprint(\”Today is {:%A}\”.format(datetime.datetime.now()))# Today is Thursday

PEP-0498: 字符串插入

这是现在推荐的字符串格式化方法,.format 太繁琐了:

12345 a = \”Hello\”b = \”World\”\”{} {}\”.format(a, b)# vs.\”%s %s\” % (a, b,)

PEP-0498致力于通过提供一些在别的语言比如Ruby,Scala,Perl中常见且存在一段时间的东西:字符串插入,来改善这种情况。这里表达式可以直接被集成到字符串,这意味着你不必再显式地调用任何额外的函数。

ES2015最近将这个特性引入到了JavaScript中,它被称为“模板字符串”:

12 const username = \”Horst\”;const welcomeMsg = `Hello, ${username}!`;

因为Python 3.0的一点历史问题,引号在Python中并不可用。再次引入也会再次影响到语言的基本语法。相代替的,另一个字符串前缀被引入: f.

1234 a = \”Hello\”b = \”World\”f\”{a} {b}\”f\”{a + \’ \’ + b}\”

你不再需要对一个字符串显式地调用 .format() 方法了,只是简单地用 f前缀标记一下格式以及内联最终字符串中你想要包括的表达式。否则它们就会提供和 .format()相同的功能。这些格式化字符串在文档中也被称为“f-strings”。

这看起来确实十分美好,但因为python 3.6会在12个月后才能够发布,你恐怕还要等上一段时间。话虽这么说,代码却已经在那儿了,所以你能做的就是获取一个python 3.6预发布版本或者使用一些像 pyenv 的小诀窍,然后让它运行就行了。

其实还有很多,这里有其他的PEP(0501),它想要引入i-strings,而这类字符串导致了字符串的懒惰计算,以至于例如你能在最终评估之前能做一些国际化(i18n)或者安全检查。虽然这个提案已延缓至进一步的讨论,但这看起来是个很好的想法。

但是回到f-strings:如果你想知道更多关于字符串插入的解决方案,看看PEP-0502 ,它包括一个关于背后的动机和来自其他语言特性灵感的详细讨论。