假设我有两个相同长度的列表:
a = ['a1', 'a2', 'a3']
b = ['b1', 'b2', 'b3']
我想生成以下字符串:
c = 'a1=b1, a2=b2, a3=b3'
实现这一目标的最佳方法是什么?
我有以下实现:
import timeit
a = [str(f) for f in range(500)]
b = [str(f) for f in range(500)]
def func1():
return ', '.join([aa+'='+bb for aa in a for bb in b if a.index(aa) == b.index(bb)])
def func2():
list = []
for i in range(len(a)):
list.append('%s=%s' % (a[i], b[i]))
return ', '.join(list)
t = timeit.Timer(setup='from __main__ import func1', stmt='func1()')
print 'func1 = ' + t.timeit(10)
t = timeit.Timer(setup='from __main__ import func2', stmt='func2()')
print 'func2 = ' + t.timeit(10)
,输出为:
func1 = 32.4704790115
func2 = 0.00529003143311
你有一些权衡吗?
答案 0 :(得分:24)
在我的系统上,这个实现比两个函数中的任何一个都快,而且更紧凑。
c = ', '.join('%s=%s' % t for t in zip(a, b))
感谢@JBernardo的建议改进。
在最近的语法中,str.format
更合适:
c = ', '.join('{}={}'.format(*t) for t in zip(a, b))
这会产生大致相同的输出,虽然它可以接受任何带有__str__
方法的对象,所以两个整数列表仍然可以在这里工作。
答案 1 :(得分:12)
a = ['a1', 'a2', 'a3']
b = ['b1', 'b2', 'b3']
pat = '%s=%%s, %s=%%s, %s=%%s'
print pat % tuple(a) % tuple(b)
给出a1=b1, a2=b2, a3=b3
然后:
from timeit import Timer
from itertools import izip
n = 300
a = [str(f) for f in range(n)]
b = [str(f) for f in range(n)]
def func1():
return ', '.join([aa+'='+bb for aa in a for bb in b if a.index(aa) == b.index(bb)])
def func2():
list = []
for i in range(len(a)):
list.append('%s=%s' % (a[i], b[i]))
return ', '.join(list)
def func3():
return ', '.join('%s=%s' % t for t in zip(a, b))
def func4():
return ', '.join('%s=%s' % t for t in izip(a, b))
def func5():
pat = n * '%s=%%s, '
return pat % tuple(a) % tuple(b)
d = dict(zip((1,2,3,4,5),('heavy','append','zip','izip','% formatting')))
for i in xrange(1,6):
t = Timer(setup='from __main__ import func%d'%i, stmt='func%d()'%i)
print 'func%d = %s %s' % (i,t.timeit(10),d[i])
结果
func1 = 16.2272833558 heavy
func2 = 0.00410247671143 append
func3 = 0.00349569568199 zip
func4 = 0.00301686387516 izip
func5 = 0.00157338432678 % formatting
答案 2 :(得分:8)
这两个解决方案非常不同的东西。第一个以嵌套方式循环,然后使用list.index
计算索引,有效地使这成为一个双重嵌套的for循环,并要求你可以想到的125,000,000个操作。第二个以锁步方式迭代,在没有进行250000次操作的情况下进行500对。难怪他们是如此不同!
您是否熟悉Big O notation来描述算法的复杂性?如果是这样,第一个解决方案是 cubic ,第二个解决方案是 linear 。选择第一个成本超过第二个成本将以惊人的速度增长a
和b
变得更长,因此没有人会使用这样的算法。
就个人而言,我几乎肯定会使用像
这样的代码', '.join('%s=%s' % pair for pair in itertools.izip(a, b))
或者如果我不太担心a
和b
的大小,只是快速写,我会使用zip
代替itertools.izip
。这段代码有几个优点
这是线性的。尽管过早优化是一个巨大的问题,但最好不要盲目地使用具有不必要的渐近性能的算法。
这很简单,也很惯用。我看到其他人经常写这样的代码。
内存效率高。通过使用生成器表达式而不是列表推导(和itertools.izip
而不是zip
),我不在内存中构建不必要的列表并转换为O(n)(线性) - 内存操作到O(1)(常数) - 记忆操作。
至于 timing 找到最快的解决方案,这几乎肯定是过早优化的一个例子。为了编写高性能程序,我们使用理论和经验编写高质量,可维护,良好的代码。经验表明,最好徒劳无功,最不利于停止随机操作并提出问题,“做这种特定操作的最佳方法是什么”,并试图通过猜测甚至测试来确定它。
实际上,具有最佳性能的程序是使用最高质量的代码和非常有选择性的优化编写的程序。重视微基准测试的可读性和简单性的高质量代码最终会更容易测试,减少错误并且更好地重构 - 这些因素是 key ,可以有效地优化您的程序。您花费时间来修复不必要的错误,理解复杂的代码,以及重新分解的斗争可以花费在优化上。
当需要优化程序时 - 经过测试并可能记录在案 - 这不是在随机片段上进行的,而是由实际用例和/或性能测试确定的,并由{{3}收集测量结果}。如果一段特定的代码只花费了0.1%的时间在程序中,那么加速该部分的任何数量都不会带来任何真正的好处。
答案 3 :(得分:3)
>>> ', '.join(i + '=' + j for i,j in zip(a,b))
'a1=b1, a2=b2, a3=b3'