Pythonstring格式:%与.format

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

  1. 以下使用每种方法,并有相同的结果,所以有什么区别?

     #!/usr/bin/python sub1 = "python string!" sub2 = "an arg" a = "i am a %s" % sub1 b = "i am a {0}".format(sub1) c = "with %(kwarg)s!" % {'kwarg':sub2} d = "with {kwarg}!".format(kwarg=sub2) print a # "i am a python string!" print b # "i am a python string!" print c # "with an arg!" print d # "with an arg!" 
  2. 此外,什么时候在Python中出现string格式? 例如,如果我的日志logging级别设置为“高”,我仍然会执行下面的%操作。 如果是这样,有没有办法避免这种情况?

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

为了回答你的第一个问题, .format在许多方面似乎更复杂。 关于%的恼人的事情也是如何可以采取variables或元组。 你会认为以下将始终工作:

 "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兼容

为了回答第二个问题,string格式化与任何其他操作同时发生 – 当string格式化expression式被评估时。 而Python不是一个懒惰的语言,因此在调用函数之前对expression式进行求值,所以在你的log.debug例子中,expression式"some debug info: %s"%some_info将首先计算,例如"some debug info: roflcopters are active" ,那么该string将被传递给log.debug()

模运算符(%)不能做的事情,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 

假设你正在使用Python的logging模块,你可以将string格式参数作为parameter passing给.debug()方法,而不是自己进行格式化:

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

这避免了格式化,除非logging器实际上logging了一些东西。

PEP 3101build议用Python 3中新的高级string格式replace%运算符,在那里它将是默认的。

从Python 3.6开始,您可以通过名称将variablesreplace为string:

 >>> 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

但是请小心,刚才我发现有一个问题,当在现有的代码中用.formatreplace所有的%'{}'.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只是一个string(在Python3中称为“字节数组”),而u是一个Unicodestring(在Python3中称为“string”):

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

当您将Unicode对象作为参数提供给%运算符时,即使原始string不是Unicode,也会生成一个Unicodestring:

 ; '{}'.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' 

只有当原始string是Unicode时,才能使用Unicode参数。

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

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

.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 {ax}, y is {ay}'.format(a=a) Out[15]: 'x is 2, y is 3' 

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

正如我今天发现的,通过%格式化string的旧方法不支持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.00000000000000000000000312375239000000009907464850 0.00000000000000000000000312375239000000000000000000

当然可能会有解决方法,但是您仍然可以考虑立即使用format()方法。

%比从我的testingformat更好的性能。

testing代码:

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的示例代码。

作为一个方面的说明,你不必采取新的风格格式与日志logging性能打。 您可以将任何对象传递给logging.debuglogging.info等实现__str__魔术方法。 当日志模块决定它必须发出你的消息对象(不pipe它是什么)时,它会在调用之前调用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 。 这提供了一个更优雅的select,在Python文档中提供的build议: https : //docs.python.org/2.6/library/logging.html#optimization 。

%可能有帮助的一种情况是当你格式化正则expression式。 例如,

 '{type_names} [az]{2}'.format(type_names='triangle|square') 

引发IndexError 。 在这种情况下,你可以使用:

 '%(type_names)s [az]{2}' % {'type_names': 'triangle|square'} 

这样可以避免将正则expression式写为'{type_names} [az]{{2}}' 。 当你有两个正则expression式,其中一个单独使用而没有格式,但是两者的连接是格式化时,这可能是有用的。