字符串格式化选项:优缺点

23

这是Python中两种非常流行的字符串格式化方法。其中一种是使用dict

>>> 'I will be %(years)i on %(month)s %(day)i' % {'years': 21, 'month': 'January', 'day': 23}
'I will be 21 on January 23'

另一个方法使用简单的元组

>>> 'I will be %i on %s %i' % (21, 'January', 23)
'I will be 21 on January 23'

第一个代码更易读,但是第二个编写速度更快。实际上我会不加区分地使用它们。

每种方法的优缺点是什么?包括性能、可读性、代码优化(它们之间是否有一种被转换成另一种)以及其他你认为有用的信息。


2
好问题。我个人认为第二个更易读。代码的可读性取决于大脑。顺便说一下,不知怎么回事,花括号总是让我不舒服。 - milancurcic
1
使用format()方法时,您无需在这两种参数传递方式之间进行选择 - 您可以同时使用两种方式(请参见我的答案)。 - Tadeck
3个回答

22

为什么format()%字符串操作更加灵活

我认为你应该坚持使用strformat()方法,因为它是格式化字符串的首选方式,并有可能在未来取代字符串格式化操作。

此外,它具有一些非常好的功能,可以将基于位置和关键字的格式化组合在一起:

>>> string = 'I will be {} years and {} months on {month} {day}'
>>> some_date = {'month': 'January', 'day': '1st'}
>>> diff = [3, 11] # years, months
>>> string.format(*diff, **some_date)
'I will be 3 years and 11 months on January 1st'

以下内容也可以正常工作:

>>> string = 'On {month} {day} it will be {1} months, {0} years'
>>> string.format(*diff, **some_date)
'On January 1st it will be 11 months, 3 years'

还有另外一个支持使用format()的原因。因为它是一个方法,所以可以像以下示例中一样将其作为回调传递:

>>> data = [(1, 2), ('a', 'b'), (5, 'ABC')]
>>> formatter = 'First is "{0[0]}", then comes "{0[1]}"'.format
>>> for item in map(formatter, data):
    print item


First is "1", then comes "2"
First is "a", then comes "b"
First is "5", then comes "ABC"

相比于字符串格式化操作,这种方法更加灵活,你可以在文档页面上查看更多例子,来对比百分号(%)操作和.format()方法的差异。

基于元组的 % 字符串格式化与基于字典的对比

通常有三种调用 % 字符串操作的方式(是的,三种而不是两种),就像这样:

base_string % values

它们的区别在于values的类型(这是由base_string的内容决定的):

  • 如果它是一个tuple,那么它们将一个接一个地被替换,按照它们在元组中出现的顺序进行替换,

    >>> 'Three first values are: %f, %f and %f' % (3.14, 2.71, 1)
    'Three first values are: 3.140000, 2.710000 and 1.000000'
    
    它可以是一个 dict(字典),然后根据关键词进行替换。
    >>> 'My name is %(name)s, I am %(age)s years old' % {'name':'John','age':98}
    'My name is John, I am 98 years old'
    
  • 如果base_string只包含一个需要插入值的位置,那么它可以是单个值:

  • >>> 'This is a string: %s' % 'abc'
    'This is a string: abc'
    

它们之间有明显的区别,这些方法不能结合使用(与format()方法相反,该方法能够结合一些功能,如上所述)。

但是有一些东西只适用于基于字典的字符串格式化操作,并且在其余三种格式化操作类型中相对不可用。这就是能够以简单的方式将特定的指示符替换为实际变量名称的能力

>>> name = 'John'
>>> surname = 'Smith'
>>> age = 87
# some code goes here
>>> 'My name is %(surname)s, %(name)s %(surname)s. I am %(age)i.' % locals()
'My name is Smith, John Smith. I am 87.'

仅供参考:当然,上述内容可以轻松地通过使用format()函数,并解包字典进行替换:

>>> 'My name is {surname}, {name} {surname}. I am {age}.'.format(**locals())
'My name is Smith, John Smith. I am 87.'

还有人知道哪种字符串格式化操作具有特定的功能,而另一种却没有吗?听起来很有趣。


3
.format()很棒。我不知道*diff的扩展,所以感谢你的提示 ;) - Blender
1
@julio.alegria:我已经更新了我的答案,并且提供了一个使用基于字典的字符串操作的示例——这种方法非常有用,而且不容易被基于元组的操作所替代。 - Tadeck
1
locals()!太厉害了!我之前也不知道。我想到一个%有而format()没有的特性,叫做“转换”(我不确定这是否是正确的名称),例如:%2.6f.. 有时候真的很有用。 - juliomalegria
@julio.alegria:谢谢!当涉及到“转换”时,我相信你指的是 M. Lutz 的《学习Python.第四版》中的以下内容:“‘浮点数支持在格式化方法调用中与%表达式中相同的类型代码和格式化特异性’。”换句话说,例如'%2.6f' % .123456789123'{:2.6f}'.format(.123456789123)都可以得到 '0.123457'。所以,基本上format()具有 %操作的这个特定功能。 - Tadeck
我在你回答中给我的链接里发现了很多有趣的特性!哇,format() 真是一个棒极了的方法!现在我明白为什么没有人提到 % 了。 - juliomalegria
显示剩余4条评论

20

我并不是在回答你的问题,只是觉得把format加入你的选择中可能会不错。

我个人认为format的语法比这两种都更好:

'I will be {years} on {month} {day}'.format(years=19, month='January', day=23)

如果我想要一些简洁的东西,我只需写:

'I will be {} on {} {}'.format(19, 'January', 23)

并且 format 对象的处理非常好:

class Birthday:
  def __init__(self, age, month, day):
    self.age = age
    self.month = month
    self.day = day

print 'I will be {b.age} on {b.month} {b.day}'.format(b = Birthday(19, 'January', 23))

1
+1 对于对象的观点很好。我也更喜欢使用 format()。我通过展示如何将两种格式化方式(正如 OP 所提到的)合并为单个调用,从而在某种程度上扩展了您的答案。 - Tadeck
两个答案都很好,我之前不太了解format()。但是,有关%格式化的任何信息呢?(那才是重点) - juliomalegria

-2

我不是在回答问题,而是在解释我在TIScript中想出的想法。

我引入了所谓的“字符串化器”函数:任何以“$”开头命名的函数都是字符串化器。编译器将“$name(”和“)”视为与函数调用组合的字符串文字的引号。

例如,这个:

$print(I will be {b.age} on {b.month} {b.day});

实际上被编译成

$print("I will be ", b.age, " on ",b.month," ",b.day);

其中偶数参数始终是文字字符串,奇数参数是表达式。通过这种方式,可以定义使用不同格式/参数处理的自定义字符串化程序。

例如,Element.$html(Hello <b>{who}</b>);将在表达式上应用HTML转义。而这个Element.$(option[value={12}]);将以jQuery样式进行选择。

非常方便和灵活。

我不确定在Python中是否可能做到这一点,而不改变它的编译器。只是作为一个想法。


1
实际上,我不认为在Python中会欢迎这样的事情,这不仅需要语法更改,而且还有一个规则说“显式优于隐式”。但是,您可以通过自定义方法/函数轻松完成您想要的操作,它们将预处理参数并将其传递到您所引用的字符串的format()方法中。甚至可以像这样: 'Hello <b>{who}</b>'.format(**escape_html(some_data)) 。不过,在Python中并不是很复杂。 - Tadeck
抱歉,我不得不给你点踩,因为你没有回答问题或发表任何与Python相关的答案。 - juliomalegria

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