performance python字符串格式化format - Python字符串格式:%与.format





python格式化输出对齐 format转义 (12)


作为一个便笺,你不需要通过日志记录来使用新的样式格式。 您可以将任何对象传递给logging.debuglogging.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#optimizationhttps://docs.python.org/2.6/library/logging.html#optimization

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中出现字符串格式? 例如,如果我的日志记录级别设置为“高”,我仍然会执行下面的%操作。 如果是这样,是否有办法避免这种情况?

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



对于Python版本> = 3.6(请参阅PEP 498

s1='albha'
s2='beta'

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

#output
'albha      beta'



%比从我的测试中得到的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获取示例代码。




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



要回答你的第一个问题...... .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()




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







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

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

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

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

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




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

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




但是请小心,刚才我发现在使用现有代码中的.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对象作为参数提供给%运算符时,即使原始字符串不是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'

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




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

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

这避免了格式化,除非记录器实际记录了某些内容。




in Python字符串和列表中

以下是一些有用的示例,它们可以说明in方法:

"foo" in "foobar"
True

"foo" in "Foobar"
False

"foo" in "Foobar".lower()
True

"foo".capitalize() in "Foobar"
True

"foo" in ["bar", "foo", "foobar"]
True

"foo" in ["fo", "o", "foobar"]
False

警告。 列表是可迭代的, in方法作用于迭代,而不仅仅是字符串。







python performance logging string-formatting