1418

Python 2.6 引入了该str.format()方法,其语法与现有%运算符略有不同。哪个更好,在什么情况下?

Python 3.6 现在通过语法f"my string". 此格式选项是否比其他格式更好?

  1. 下面使用每种方法,结果都一样,那么有什么区别呢?

     #!/usr/bin/python
     sub1 = "python string!"
     sub2 = "an arg"
    
     sub_a = "i am a %s" % sub1
     sub_b = "i am a {0}".format(sub1)
     sub_c = f"i am a {sub1}"
    
     arg_a = "with %(kwarg)s!" % {'kwarg':sub2}
     arg_b = "with {kwarg}!".format(kwarg=sub2)
     arg_c = f"with {sub2}!"
    
     print(sub_a)    # "i am a python string!"
     print(sub_b)    # "i am a python string!"
     print(sub_c)    # "i am a python string!"
    
     print(arg_a)    # "with an arg!"
     print(arg_b)    # "with an arg!"
     print(arg_c)    # "with an arg!"
    
  2. 此外,字符串格式化何时出现在 Python 中?例如,如果我的日志记录级别设置为 HIGH,我仍然会因为执行以下%操作而受到打击吗?如果是这样,有没有办法避免这种情况?

     log.debug("some debug info: %s" % some_info)
    
4

16 回答 16

977

要回答您的第一个问题…….format在许多方面似乎更加复杂。一个令人讨厌的事情%是它如何接受一个变量或一个元组。你会认为以下方法总是有效的:

"hi there %s" % name

然而,如果name碰巧是(1, 2, 3),它会抛出一个TypeError. 为了保证它总是打印,你需要做

"hi there %s" % (name,)   # supply the single argument as a single-item tuple

这很丑陋。.format没有这些问题。同样在你给出的第二个例子中,这个.format例子看起来更干净。

你为什么不使用它?

  • 不知道(在阅读本文之前我)
  • 必须与 Python 2.5 兼容

要回答您的第二个问题,字符串格式化与任何其他操作同时发生 - 在评估字符串格式化表达式时。并且 Python 不是一种惰性语言,它在调用函数之前评估表达式,因此在您的log.debug示例中,表达式"some debug info: %s"%some_info将首先评估为,例如"some debug info: roflcopters are active",然后该字符串将传递给log.debug()

于 2011-02-22T18:49:21.347 回答
316

模运算符( % )不能做的事情,afaik:

tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)

结果

12 22222 45 22222 103 22222 6 22222

很有用。

还有一点:format(),作为一个函数,可以在其他函数中用作参数:

li = [12,45,78,784,2,69,1254,4785,984]
print map('the number is {}'.format,li)   

print

from datetime import datetime,timedelta

once_upon_a_time = datetime(2010, 7, 1, 12, 0, 0)
delta = timedelta(days=13, hours=8,  minutes=20)

gen =(once_upon_a_time +x*delta for x in xrange(20))

print '\n'.join(map('{:%Y-%m-%d %H:%M:%S}'.format, gen))

结果是:

['the number is 12', 'the number is 45', 'the number is 78', 'the number is 784', 'the number is 2', 'the number is 69', 'the number is 1254', 'the number is 4785', 'the number is 984']

2010-07-01 12:00:00
2010-07-14 20:20:00
2010-07-28 04:40:00
2010-08-10 13:00:00
2010-08-23 21:20:00
2010-09-06 05:40:00
2010-09-19 14:00:00
2010-10-02 22:20:00
2010-10-16 06:40:00
2010-10-29 15:00:00
2010-11-11 23:20:00
2010-11-25 07:40:00
2010-12-08 16:00:00
2010-12-22 00:20:00
2011-01-04 08:40:00
2011-01-17 17:00:00
2011-01-31 01:20:00
2011-02-13 09:40:00
2011-02-26 18:00:00
2011-03-12 02:20:00
于 2011-06-13T20:20:32.467 回答
156

假设您使用的是 Python 的logging模块,您可以将字符串格式化参数作为参数传递给.debug()方法,而不是自己进行格式化:

log.debug("some debug info: %s", some_info)

除非记录器实际记录某些内容,否则可以避免进行格式化。

于 2011-02-22T19:21:07.580 回答
132

从 Python 3.6 (2016) 开始,您可以使用f-strings替换变量:

>>> origin = "London"
>>> destination = "Paris"
>>> f"from {origin} to {destination}"
'from London to Paris'

注意f"前缀。如果您在 Python 3.5 或更早版本中尝试此操作,您将获得一个SyntaxError.

请参阅https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings

于 2016-04-15T11:12:07.080 回答
63

PEP 3101建议%用 Python 3 中新的高级字符串格式替换运算符,这将是默认值。

于 2011-08-01T03:01:34.850 回答
59

但是请注意,刚才我在尝试%.format现有代码替换所有代码时发现了一个问题:'{}'.format(unicode_string)将尝试编码 unicode_string 并且可能会失败。

看看这个 Python 交互式会话日志:

Python 2.7.2 (default, Aug 27 2012, 19:52:55) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
; s='й'
; u=u'й'
; s
'\xd0\xb9'
; u
u'\u0439'

s只是一个字符串(在 Python3 中称为“字节数组”)并且u是一个 Unicode 字符串(在 Python3 中称为“字符串”):

; '%s' % s
'\xd0\xb9'
; '%s' % u
u'\u0439'

当您将 Unicode 对象作为参数提供给%operator 时,即使原始字符串不是 Unicode,它也会生成一个 Unicode 字符串:

; '{}'.format(s)
'\xd0\xb9'
; '{}'.format(u)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character u'\u0439' in position 0: ordinal not in range(256)

但该.format函数会引发“UnicodeEncodeError”:

; u'{}'.format(s)
u'\xd0\xb9'
; u'{}'.format(u)
u'\u0439'

只有当原始字符串是 Unicode 时,它​​才会与 Unicode 参数一起正常工作。

; '{}'.format(u'i')
'i'

或者如果参数字符串可以转换为字符串(所谓的“字节数组”)

于 2012-09-03T18:15:42.977 回答
46

%提供比format我的测试更好的性能。

测试代码:

Python 2.7.2:

import timeit
print 'format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')")
print '%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')")

结果:

> format: 0.470329046249
> %: 0.357107877731

Python 3.5.2

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))

结果

> format: 0.5864730989560485
> %: 0.013593495357781649

它看起来在 Python2 中,差异很小,而在 Python3 中,%format.

感谢@Chris Cogdon 提供示例代码。

编辑1:

2019 年 7 月在 Python 3.7.2 中再次测试。

结果:

> format: 0.86600608
> %: 0.630180146

没有太大区别。我猜 Python 正在逐渐改进。

编辑2:

在有人在评论中提到了 python 3 的 f-string 之后,我在 python 3.7.2 下对以下代码进行了测试:

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))
print('f-string:', timeit.timeit("f'{1}{1.23}{\"hello\"}'"))

结果:

format: 0.8331376779999999
%: 0.6314778750000001
f-string: 0.766649943

似乎 f-string 仍然%format.

于 2011-06-13T18:43:10.627 回答
38

另一个优点.format(我在答案中没有看到):它可以采用对象属性。

In [12]: class A(object):
   ....:     def __init__(self, x, y):
   ....:         self.x = x
   ....:         self.y = y
   ....:         

In [13]: a = A(2,3)

In [14]: 'x is {0.x}, y is {0.y}'.format(a)
Out[14]: 'x is 2, y is 3'

或者,作为关键字参数:

In [15]: 'x is {a.x}, y is {a.y}'.format(a=a)
Out[15]: 'x is 2, y is 3'

%据我所知,这是不可能的。

于 2014-12-04T18:33:46.877 回答
33

正如我今天发现的那样,格式化字符串的旧方法%不支持Decimal开箱即用的 Python 用于十进制定点和浮点运算的模块。

示例(使用 Python 3.3.5):

#!/usr/bin/env python3

from decimal import *

getcontext().prec = 50
d = Decimal('3.12375239e-24') # no magic number, I rather produced it by banging my head on my keyboard

print('%.50f' % d)
print('{0:.50f}'.format(d))

输出:

0.000000000000000000000000312375239000000009907464850 0.00000000000000000000000312375239000000000000000000

肯定有解决方法,但您仍然可以考虑format()立即使用该方法。

于 2014-05-13T17:10:00.563 回答
25

如果您的 python >= 3.6,F 字符串格式的文字是您的新朋友。

它更简单,更干净,性能更好。

In [1]: params=['Hello', 'adam', 42]

In [2]: %timeit "%s %s, the answer to everything is %d."%(params[0],params[1],params[2])
448 ns ± 1.48 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit "{} {}, the answer to everything is {}.".format(*params)
449 ns ± 1.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [4]: %timeit f"{params[0]} {params[1]}, the answer to everything is {params[2]}."
12.7 ns ± 0.0129 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
于 2018-07-04T07:13:44.810 回答
16

附带说明一下,您不必对性能造成影响就可以使用带有日志记录的新样式格式。您可以将任何实现魔术方法的对象传递给logging.debug,logging.info等。__str__当日志记录模块决定它必须发出您的消息对象(无论它是什么)时,它会str(message_object)在这样做之前调用。所以你可以做这样的事情:

import logging


class NewStyleLogMessage(object):
    def __init__(self, message, *args, **kwargs):
        self.message = message
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        args = (i() if callable(i) else i for i in self.args)
        kwargs = dict((k, v() if callable(v) else v) for k, v in self.kwargs.items())

        return self.message.format(*args, **kwargs)

N = NewStyleLogMessage

# Neither one of these messages are formatted (or calculated) until they're
# needed

# Emits "Lazily formatted log entry: 123 foo" in log
logging.debug(N('Lazily formatted log entry: {0} {keyword}', 123, keyword='foo'))


def expensive_func():
    # Do something that takes a long time...
    return 'foo'

# Emits "Expensive log entry: foo" in log
logging.debug(N('Expensive log entry: {keyword}', keyword=expensive_func))

这在 Python 3 文档 ( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles )中都有描述。但是,它也适用于 Python 2.6 ( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages )。

使用这种技术的优点之一,除了它的格式风格不可知,它允许惰性值,例如expensive_func上面的函数。这为此处的 Python 文档中给出的建议提供了一个更优雅的替代方案:https ://docs.python.org/2.6/library/logging.html#optimization 。

于 2014-08-21T18:00:48.120 回答
12

一种%可能会有所帮助的情况是当您格式化正则表达式时。例如,

'{type_names} [a-z]{2}'.format(type_names='triangle|square')

提高IndexError。在这种情况下,您可以使用:

'%(type_names)s [a-z]{2}' % {'type_names': 'triangle|square'}

这避免了将正则表达式写为'{type_names} [a-z]{{2}}'. 当您有两个正则表达式时,这可能很有用,其中一个单独使用而没有格式,但两者的连接已格式化。

于 2015-04-09T20:41:40.623 回答
8

我要补充一点,从 3.6 版开始,我们可以使用 fstrings,如下所示

foo = "john"
bar = "smith"
print(f"My name is {foo} {bar}")

哪个给

我的名字是约翰史密斯

一切都转换为字符串

mylist = ["foo", "bar"]
print(f"mylist = {mylist}")

结果:

mylist = ['foo', 'bar']

你可以传递函数,就像其他格式的方法一样

print(f'Hello, here is the date : {time.strftime("%d/%m/%Y")}')

举个例子

你好,这里是日期:16/04/2018

于 2018-04-16T14:42:06.267 回答
6

Python 3.6.7 对比:

#!/usr/bin/env python
import timeit

def time_it(fn):
    """
    Measure time of execution of a function
    """
    def wrapper(*args, **kwargs):
        t0 = timeit.default_timer()
        fn(*args, **kwargs)
        t1 = timeit.default_timer()
        print("{0:.10f} seconds".format(t1 - t0))
    return wrapper


@time_it
def new_new_format(s):
    print("new_new_format:", f"{s[0]} {s[1]} {s[2]} {s[3]} {s[4]}")


@time_it
def new_format(s):
    print("new_format:", "{0} {1} {2} {3} {4}".format(*s))


@time_it
def old_format(s):
    print("old_format:", "%s %s %s %s %s" % s)


def main():
    samples = (("uno", "dos", "tres", "cuatro", "cinco"), (1,2,3,4,5), (1.1, 2.1, 3.1, 4.1, 5.1), ("uno", 2, 3.14, "cuatro", 5.5),) 
    for s in samples:
        new_new_format(s)
        new_format(s)
        old_format(s)
        print("-----")


if __name__ == '__main__':
    main()

输出:

new_new_format: uno dos tres cuatro cinco
0.0000170280 seconds
new_format: uno dos tres cuatro cinco
0.0000046750 seconds
old_format: uno dos tres cuatro cinco
0.0000034820 seconds
-----
new_new_format: 1 2 3 4 5
0.0000043980 seconds
new_format: 1 2 3 4 5
0.0000062590 seconds
old_format: 1 2 3 4 5
0.0000041730 seconds
-----
new_new_format: 1.1 2.1 3.1 4.1 5.1
0.0000092650 seconds
new_format: 1.1 2.1 3.1 4.1 5.1
0.0000055340 seconds
old_format: 1.1 2.1 3.1 4.1 5.1
0.0000052130 seconds
-----
new_new_format: uno 2 3.14 cuatro 5.5
0.0000053380 seconds
new_format: uno 2 3.14 cuatro 5.5
0.0000047570 seconds
old_format: uno 2 3.14 cuatro 5.5
0.0000045320 seconds
-----
于 2019-02-05T09:56:38.557 回答
5

对于 python 版本 >= 3.6(参见PEP 498

s1='albha'
s2='beta'

f'{s1}{s2:>10}'

#output
'albha      beta'
于 2018-02-14T22:42:32.920 回答
3

但有一件事是,如果您有嵌套的花括号,则不适用于格式,但%可以使用。

例子:

>>> '{{0}, {1}}'.format(1,2)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    '{{0}, {1}}'.format(1,2)
ValueError: Single '}' encountered in format string
>>> '{%s, %s}'%(1,2)
'{1, 2}'
>>> 
于 2018-08-25T01:08:57.227 回答