Python,优化字符串连接的列表理解
Python, optimizing a list comprehension for string concatenation
我在通过列表理解处理字符串的脚本上使用 cProfile
到 运行 基准测试。我要优化的目标行如下所示:
signals = [('prefix' + str(day) + '_' + s) for s in signals]
其中 day
是一个整数。
这条特定的线路占用了整个 运行 时间的 33%。当然,它在执行过程中调用了百万次。
我尝试了几个明显的替代方案,包括 ''.join('prefix', str(day), '_', s)
、'prefix{:}_'.format(day)
,并使用 numpy.core.defchararray.add
连接通过 numpy.tile
与 [=18 创建的前缀数组=].它们都比列表理解慢 2 倍。
这条线路还有速度优化空间吗?
你试过了吗:
signals = ['prefix{0}_{1}'.format(day, s) for s in signals]
作为基准,如果您只使用元组而不是字符串会怎样?:
signals = [(day, s) for s in signals]
这应该至少带来最小的改进:
# First precalculate the static part of the string
template = 'prefix%s_' % day + '%s'
# Then, use the %s string interpolation instead of joining strings with '+'
# -->> Note: this proved to be wrong later...
signals = [template % s for s in signals]
# Alternatively you could use map to compare efficiency (be aware of differences between python 2 and 3)
signals = map(template.__mod__, signals)
str.format
的插值比%s
的插值贵,所以我不打算尝试
现在让我们比较一下时间。你的方法:
>>> import timeit
>>> day = 45
>>> signals = ['aaaa', 'bbbb', 'cccccc', 'dddddddddddd']
>>> timeit.timeit("[('prefix' + str(day) + '_' + s) for s in signals]", 'from __main__ import day, signals')
1.35095184709592
我的第一个方法
>>> template = 'prefix%s_' % day + '%s'
>>> timeit.timeit("[template % s for s in signals]", 'from __main__ import template, signals')
0.7075940089748229
我的第二种方法
>>> timeit.timeit("map(template.__mod__, signals)", 'from __main__ import template, signals')
0.9939713030159822
所以,带有列表理解的模板的预计算似乎赢了。还有更多的事情需要考虑,例如,如果一个发电机对你来说足够好。
EDIT 从有趣的评论中指出的信息中,我添加了另一个解决方案:在紧密循环内部,我们只将两个字符串连接在一起,因此我们可以直接连接它们% 格式化
>>> template = 'prefix%s_' % day
>>> timeit.timeit("[template + s for s in signals]", 'from __main__ import template, signals')
0.39771016975851126
目前谁是赢家。
这样会更快
import numpy as np
signal = [x for x in range(9000)]
length = len(signal)
prefArray = np.array(['prefix']*length )
dArray = np.array([str(day)]*length )
cArray = np.array(['_']*length )
sArray = np.array(["%s"%x for x in signal])
firstArray = np.core.defchararray.add(prefArray, dArray)
secondArray = np.core.defchararray.add(cArray, sArray)
result = np.core.defchararray.add(firstArray,secondArray)
print result
我在通过列表理解处理字符串的脚本上使用 cProfile
到 运行 基准测试。我要优化的目标行如下所示:
signals = [('prefix' + str(day) + '_' + s) for s in signals]
其中 day
是一个整数。
这条特定的线路占用了整个 运行 时间的 33%。当然,它在执行过程中调用了百万次。
我尝试了几个明显的替代方案,包括 ''.join('prefix', str(day), '_', s)
、'prefix{:}_'.format(day)
,并使用 numpy.core.defchararray.add
连接通过 numpy.tile
与 [=18 创建的前缀数组=].它们都比列表理解慢 2 倍。
这条线路还有速度优化空间吗?
你试过了吗:
signals = ['prefix{0}_{1}'.format(day, s) for s in signals]
作为基准,如果您只使用元组而不是字符串会怎样?:
signals = [(day, s) for s in signals]
这应该至少带来最小的改进:
# First precalculate the static part of the string
template = 'prefix%s_' % day + '%s'
# Then, use the %s string interpolation instead of joining strings with '+'
# -->> Note: this proved to be wrong later...
signals = [template % s for s in signals]
# Alternatively you could use map to compare efficiency (be aware of differences between python 2 and 3)
signals = map(template.__mod__, signals)
str.format
的插值比%s
的插值贵,所以我不打算尝试
现在让我们比较一下时间。你的方法:
>>> import timeit
>>> day = 45
>>> signals = ['aaaa', 'bbbb', 'cccccc', 'dddddddddddd']
>>> timeit.timeit("[('prefix' + str(day) + '_' + s) for s in signals]", 'from __main__ import day, signals')
1.35095184709592
我的第一个方法
>>> template = 'prefix%s_' % day + '%s'
>>> timeit.timeit("[template % s for s in signals]", 'from __main__ import template, signals')
0.7075940089748229
我的第二种方法
>>> timeit.timeit("map(template.__mod__, signals)", 'from __main__ import template, signals')
0.9939713030159822
所以,带有列表理解的模板的预计算似乎赢了。还有更多的事情需要考虑,例如,如果一个发电机对你来说足够好。
EDIT 从有趣的评论中指出的信息中,我添加了另一个解决方案:在紧密循环内部,我们只将两个字符串连接在一起,因此我们可以直接连接它们% 格式化
>>> template = 'prefix%s_' % day
>>> timeit.timeit("[template + s for s in signals]", 'from __main__ import template, signals')
0.39771016975851126
目前谁是赢家。
这样会更快
import numpy as np
signal = [x for x in range(9000)]
length = len(signal)
prefArray = np.array(['prefix']*length )
dArray = np.array([str(day)]*length )
cArray = np.array(['_']*length )
sArray = np.array(["%s"%x for x in signal])
firstArray = np.core.defchararray.add(prefArray, dArray)
secondArray = np.core.defchararray.add(cArray, sArray)
result = np.core.defchararray.add(firstArray,secondArray)
print result