Python 字符串格式化太慢

8 投票
3 回答
3565 浏览
提问于 2025-04-15 21:35

我用下面的代码来记录一个地图(map),当地图里只有零的时候,速度很快,但一旦里面有了实际的数据,速度就变得非常慢……有没有什么办法可以让这个过程更快一些呢?

log_file = open('testfile', 'w')
for i, x in ((i, start + i * interval) for i in range(length)):
    log_file.write('%-5d %8.3f %13g %13g %13g %13g %13g %13g\n' % (i, x,
        map[0][i], map[1][i], map[2][i], map[3][i], map[4][i], map[5][i]))

3 个回答

0

我不想深入讨论如何优化这段代码,但如果是我写的话,我会这样写:

log_file = open('testfile', 'w')
x = start
map_iter = zip(range(length), map[0], map[1], map[2], map[3], map[4], map[5])
fmt = '%-5d %8.3f %13g %13g %13g %13g %13g %13g\n'
for i, m0, m1, m2, m3, m4, m5 in mapiter:
    s = fmt % (i, x, m0, m1, m2, m3, m4, m5)
    log_file.write(s)
    x += interval

不过我想提醒一下,最好不要用Python自带的名字来命名变量,比如说 map

2

首先,我对比了使用 % 和反引号的速度。结果发现 % 更快。接着,我又对比了 %(元组)和 'string'.format() 的速度。最开始我以为后者更快,但其实不是,% 还是更快。

所以,你现在在 Python 中进行大量的浮点数转字符串的操作,已经是最快的方法了。

下面的示例代码写得很糟糕,请不要给我讲 xrange 和 range 的区别,或者其他那些小细节。谢谢,再见。

我的非正式测试显示,在 Linux 上使用 Python 2.5 的 %(1.234,)操作比在 Linux 上使用 Python 2.6 的 %(1.234,...)操作要快,前提是 'string'.format() 在 2.6 之前的版本是无法使用的。还有其他类似的情况。

# this code should never be used in production.
# should work on linux and windows now.

import random
import timeit
import os
import tempfile


start = 0
interval = 0.1

amap = [] # list of lists
tmap = [] # list of tuples

def r():
    return random.random()*500

for i in xrange(0,10000):
        amap.append ( [r(),r(),r(),r(),r(),r()] )

for i in xrange(0,10000):
        tmap.append ( (r(),r(),r(),r(),r(),r()) )




def testme_percent():
    log_file = tempfile.TemporaryFile()
    try:
        for qmap in amap:
            s = '%g %g %g %g %g %g \n' % (qmap[0], qmap[1], qmap[2], qmap[3], qmap[4], qmap[5]) 
            log_file.write( s)
    finally:
        log_file.close();

def testme_tuple_percent():
    log_file = tempfile.TemporaryFile()
    try:    
        for qtup in tmap:
            s = '%g %g %g %g %g %g \n' % qtup
            log_file.write( s );
    finally:
        log_file.close();

def testme_backquotes_rule_yeah_baby():
    log_file = tempfile.TemporaryFile()
    try:
        for qmap in amap:
            s = `qmap`+'\n'
            log_file.write( s );
    finally:
        log_file.close();        

def testme_the_new_way_to_format():
    log_file = tempfile.TemporaryFile()
    try:
        for qmap in amap:
            s = '{0} {1} {2} {3} {4} {5} \n'.format(qmap[0], qmap[1], qmap[2], qmap[3], qmap[4], qmap[5]) 
            log_file.write( s );
    finally:
        log_file.close();

# python 2.5 helper
default_number = 50 
def _xtimeit(stmt="pass",  timer=timeit.default_timer,
           number=default_number):
    """quick and dirty"""
    if stmt<>"pass":
        stmtcall = stmt+"()"
        ssetup = "from __main__ import "+stmt
    else:
        stmtcall = stmt
        ssetup = "pass"
    t = timeit.Timer(stmtcall,setup=ssetup)
    try:
      return t.timeit(number)
    except:
      t.print_exc()


# no formatting operation in testme2

print "now timing variations on a theme"

#times = []
#for i in range(0,10):

n0 = _xtimeit( "pass",number=50)
print "pass = ",n0

n1 = _xtimeit( "testme_percent",number=50);
print "old style % formatting=",n1

n2 = _xtimeit( "testme_tuple_percent",number=50);
print "old style % formatting with tuples=",n2

n3 = _xtimeit( "testme_backquotes_rule_yeah_baby",number=50);
print "backquotes=",n3

n4 = _xtimeit( "testme_the_new_way_to_format",number=50);
print "new str.format conversion=",n4


#        times.append( n);




print "done"    

我认为你可以通过在其他地方构建浮点数的元组来优化你的代码,比如在你创建那个映射的时候,先构建好元组列表,然后用 fmt_string % tuple 这种方式来应用。

for tup in mytups:
    log_file.write( fmt_str % tup )

我通过把创建元组的部分从循环中移出去,把 8.7 秒的时间缩短到了 8.5 秒。虽然这变化不大,但主要的耗时还是在浮点数格式化上,我觉得这部分总是比较耗费时间。

另外一个建议:

你有没有考虑过不把这么大的日志直接写成文本,而是使用最快的“持久化”方法来保存它们,然后在需要的时候再写一个小工具把它们转成文本?有些人使用 NumPy 来处理非常大的数字数据集,似乎并不会逐行存储他们的数据。可以参考一下:

http://thsant.blogspot.com/2007/11/saving-numpy-arrays-which-is-fastest.html

3

我建议你使用 cProfile 模块来运行你的代码,然后按照 这个链接 上的说明处理结果。这样你就能清楚地知道在字符串格式化时,调用 str.__mod__ 花费了多少时间,以及其他操作,比如写文件和查找 map[0][i] 等,花费了多少时间。

撰写回答