防止列表理解中的多次调用
Preventing multiple calls in list comprehension
以下面的例子为例,我们可以看到 x.giveMyNum() 将被调用 4 次 - 3 次检查 myNum 的值,一次构建 return 的列表。你可能希望它只被调用 3 次,因为它是一个纯函数并且它的值不会改变。
列表理解版本:
class test(object):
def __init__(self,myNum):
self.myNum=myNum
def giveMyNum(self):
print "giving"
return self.myNum
q=[test(x) for x in range(3)]
print [x.giveMyNum() for x in q if x.giveMyNum()>1]
我知道你可以这样做来修复它:
ret=[]
for x in q:
k=x.giveMyNum()
if k>1:
ret.append(k)
但是有没有办法防止列表理解中的额外调用?
我不需要保留中间值。
您可以将它与生成器结合使用,但我会坚持使用常规循环。
print([n for n in (x.giveMyNum() for x in q) if n > 1 ])
如果您喜欢功能代码,您可以映射或 itertools.imap
与 python2:
print([n for n in map(test.giveMyNum, q) if n > 1 ])
使用 python2 和 imap 比 gen exp 更快:
In [8]: q = [test(x) for x in range(10000)]
In [9]: timeit [ n for n in imap(test.giveMyNum, q) if n > 1]
1000 loops, best of 3: 1.94 ms per loop
In [10]: timeit [n for n in (x.giveMyNum() for x in q) if n > 1 ]
100 loops, best of 3: 2.56 ms per loop
地图也更快使用 python3:
In [2]: timeit [ n for n in map(test.giveMyNum, q) if n > 1]
100 loops, best of 3: 2.23 ms per loop
In [3]: timeit [n for n in (x.giveMyNum() for x in q) if n > 1 ]
100 loops, best of 3: 2.93 ms per loop
正则循环和调用方法的时间:
In [8]: timeit [x.giveMyNum() for x in q if x.giveMyNum()>1]
100 loops, best of 3: 3.59 ms per loop
In [9]: %%timeit
ret=[]
for x in q:
k=x.giveMyNum()
if k>1:
ret.append(k)
...:
100 loops, best of 3: 2.67 ms per loop
Python3:
In [2]: %%timeit
ret=[]
for x in q:
k=x.giveMyNum()
if k>1:
ret.append(k)
...:
100 loops, best of 3: 2.84 ms per loop
In [3]: timeit [x.giveMyNum() for x in q if x.giveMyNum()>1]
100 loops, best of 3: 4.08 ms per loop
有可能,但可读性不强...
print [y for y in (x.giveMyNum() for x in q) if y > 1]
所以除非 giveMyNum()
很慢或不纯,否则我会避免它。
以下面的例子为例,我们可以看到 x.giveMyNum() 将被调用 4 次 - 3 次检查 myNum 的值,一次构建 return 的列表。你可能希望它只被调用 3 次,因为它是一个纯函数并且它的值不会改变。
列表理解版本:
class test(object):
def __init__(self,myNum):
self.myNum=myNum
def giveMyNum(self):
print "giving"
return self.myNum
q=[test(x) for x in range(3)]
print [x.giveMyNum() for x in q if x.giveMyNum()>1]
我知道你可以这样做来修复它:
ret=[]
for x in q:
k=x.giveMyNum()
if k>1:
ret.append(k)
但是有没有办法防止列表理解中的额外调用?
我不需要保留中间值。
您可以将它与生成器结合使用,但我会坚持使用常规循环。
print([n for n in (x.giveMyNum() for x in q) if n > 1 ])
如果您喜欢功能代码,您可以映射或 itertools.imap
与 python2:
print([n for n in map(test.giveMyNum, q) if n > 1 ])
使用 python2 和 imap 比 gen exp 更快:
In [8]: q = [test(x) for x in range(10000)]
In [9]: timeit [ n for n in imap(test.giveMyNum, q) if n > 1]
1000 loops, best of 3: 1.94 ms per loop
In [10]: timeit [n for n in (x.giveMyNum() for x in q) if n > 1 ]
100 loops, best of 3: 2.56 ms per loop
地图也更快使用 python3:
In [2]: timeit [ n for n in map(test.giveMyNum, q) if n > 1]
100 loops, best of 3: 2.23 ms per loop
In [3]: timeit [n for n in (x.giveMyNum() for x in q) if n > 1 ]
100 loops, best of 3: 2.93 ms per loop
正则循环和调用方法的时间:
In [8]: timeit [x.giveMyNum() for x in q if x.giveMyNum()>1]
100 loops, best of 3: 3.59 ms per loop
In [9]: %%timeit
ret=[]
for x in q:
k=x.giveMyNum()
if k>1:
ret.append(k)
...:
100 loops, best of 3: 2.67 ms per loop
Python3:
In [2]: %%timeit
ret=[]
for x in q:
k=x.giveMyNum()
if k>1:
ret.append(k)
...:
100 loops, best of 3: 2.84 ms per loop
In [3]: timeit [x.giveMyNum() for x in q if x.giveMyNum()>1]
100 loops, best of 3: 4.08 ms per loop
有可能,但可读性不强...
print [y for y in (x.giveMyNum() for x in q) if y > 1]
所以除非 giveMyNum()
很慢或不纯,否则我会避免它。