有多种字符串格式设置方法:
Python<2.6:“您好%s”%namePython 2.6+:“Hello{}”.format(name)(使用str.format)Python 3.6+:f“{name}”(使用f-string)
哪种情况更好?在什么情况下?
以下方法具有相同的结果,那么有什么区别?name=“爱丽丝”“你好%s”%name“您好{0}”.format(名称)f“您好{name}”#使用命名参数:“您好%(kwarg)s”%{'kwarg':name}“你好{kwarg}”.format(kwarg=name)f“您好{name}”字符串格式化何时运行,如何避免运行时性能损失?
如果您试图结束一个重复的问题,该问题只是在寻找一种格式化字符串的方法,请使用How do I put a variable value in a string?。
如果您的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)
顺便说一句,在日志记录中使用新样式的格式并不一定会影响性能。您可以将任何对象传递给实现__str__魔术方法的logging.debug、logging.info等。当日志模块决定必须发出消息对象(无论是什么)时,它会在发出消息之前调用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-样式)。但是,它也可以与Python 2.6一起使用(https://docs.python.org/2.6/library/logging.html#using-作为消息的任意对象)。
使用此技术的一个优点是,它允许延迟值,例如上面的函数expensive_func,而不是格式化样式不可知。这为Python文档中给出的建议提供了一个更优雅的替代方案:https://docs.python.org/2.6/library/logging.html#optimization.
但有一点是,如果您有嵌套的大括号,则不适用于格式,但%可以使用。
例子:
>>> '{{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}'
>>>
回答第一个问题。格式在许多方面似乎更为复杂。关于%的一个令人讨厌的问题是,它可以接受变量或元组。你会认为以下方法总是有效的:
"Hello %s" % name
然而,如果name恰好是(1,2,3),它将抛出一个TypeError。为了保证它总是打印出来,你需要
"Hello %s" % (name,) # supply the single argument as a single-item tuple
这太难看了。格式没有这些问题。同样在您给出的第二个示例中,.format示例看起来更简洁。
仅用于向后兼容Python 2.5。
为了回答第二个问题,字符串格式化与任何其他操作同时发生-当计算字符串格式化表达式时。Python不是一种惰性语言,它在调用函数之前会对表达式求值,因此表达式log.debug(“somedebuginfo:%s”%some_info)将首先将字符串求值为,例如“somedebug-info:roflcopters is active”,然后将该字符串传递给log.debug()。
但是请注意,刚才我在尝试用现有代码中的.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中称为“byte array”),u是一个Unicode字符串(在Python 3中称“string”):
; '%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'
或者如果参数字符串可以转换为字符串(称为“字节数组”)