一个班轮通过一个可迭代的(生成器)
one liner to go through an iterable (generator)
我遇到了一些代码,如下所示:
[func(val) for val in iterable]
有一个可迭代对象(在我的例子中是一个生成器),用户希望为其每个值调用一个函数来解决它的副作用(例如,func
可能只是 print
)但是其中 return 值无关紧要。
我不喜欢这种方法的一点是,创建了一个临时的 list
,如果生成器产生大量值,这可能会消耗相当多的内存。
如果 func
的 return 值始终计算为 False
,则以下工作:
any(func(val) for val in iterable)
如果 func 的 return 值始终计算为 True
,则以下工作:
all(func(val) for val in iterable)
如果 func 的 return 值可以计算为 True
或 False
,我该怎么办
还有什么比将值强制为 False 更好的吗?
我想到的最好的是:
any(func(val) and False for val in iterable)
或
all(func(val) or True for val in iterable)
可能只是
for val in iterable:
func(val)
最清楚。
for val in iterable: func(val)
如果确实需要单线,则可用。
使用带有 bool
函数的 set
怎么样?
{bool(func(val)) for val in iterable}
已编辑:
看了@gelonida的分析,相信下面的速度会快一些。
{None for val in iterable(N) if func(val)}
只是给定答案/潜在解决方案的综合和时序分析
@LeopardShark 的答案似乎是最短、最易读的答案。
并且名列前茅。 (时间不准确,我没有看字节码)
速度明智
- @LeopardShark 的回答
- @ywbaek 的第二个建议
- 初始代码,如果 N 不是太大 (~10000)
- 我在问题中发布的建议
初始代码存在白白分配释放内存的缺点
我在问题中建议的代码的缺点是理解起来不太直观,如果弄乱了 (all
, any
) 和 (and False
, or True
) 可能无法按预期执行所有操作,而且性能也稍差
@ywbaek 的解决方案比我的建议更安全,也更直观,但执行速度更快。
最简单的解决方案有一个小缺点,即不能用作 lambda。
我的时间代码:
N=10000
M=500
called = 0
def func(v):
global called
called += 1
v * v * v * v * v *v / (v+0.1)
def iterable(N):
for v in range(N):
v * 2
yield v
def testrun():
global called
called=0
print(timeit(test, number=M), end=" ")
print(called)
print("consume some CPU")
timeit(lambda: 3**.5 **.2) # it seems, that timeit is a little more predictable if I let the process warm up a little
print("Start measures")
def test():
for val in iterable(N): func(val)
testrun()
def test():
{None for val in iterable(N) if func(val)}
testrun()
def test():
[func(val) for val in iterable(N)]
testrun()
def test():
all(func(val) or True for val in iterable(N))
testrun()
def test():
any(func(val) and False for val in iterable(N))
testrun()
我的旧电脑上的结果:
consume some CPU
Start measures
3.864932143012993 5000000
3.916696268017404 5000000
4.0817033689818345 5000000
4.293206526956055 5000000
4.319622751965653 5000000
我遇到了一些代码,如下所示:
[func(val) for val in iterable]
有一个可迭代对象(在我的例子中是一个生成器),用户希望为其每个值调用一个函数来解决它的副作用(例如,func
可能只是 print
)但是其中 return 值无关紧要。
我不喜欢这种方法的一点是,创建了一个临时的 list
,如果生成器产生大量值,这可能会消耗相当多的内存。
如果 func
的 return 值始终计算为 False
,则以下工作:
any(func(val) for val in iterable)
如果 func 的 return 值始终计算为 True
,则以下工作:
all(func(val) for val in iterable)
如果 func 的 return 值可以计算为 True
或 False
还有什么比将值强制为 False 更好的吗?
我想到的最好的是:
any(func(val) and False for val in iterable)
或
all(func(val) or True for val in iterable)
可能只是
for val in iterable:
func(val)
最清楚。
for val in iterable: func(val)
如果确实需要单线,则可用。
使用带有 bool
函数的 set
怎么样?
{bool(func(val)) for val in iterable}
已编辑:
看了@gelonida的分析,相信下面的速度会快一些。
{None for val in iterable(N) if func(val)}
只是给定答案/潜在解决方案的综合和时序分析
@LeopardShark 的答案似乎是最短、最易读的答案。 并且名列前茅。 (时间不准确,我没有看字节码)
速度明智 - @LeopardShark 的回答 - @ywbaek 的第二个建议 - 初始代码,如果 N 不是太大 (~10000) - 我在问题中发布的建议
初始代码存在白白分配释放内存的缺点
我在问题中建议的代码的缺点是理解起来不太直观,如果弄乱了 (all
, any
) 和 (and False
, or True
) 可能无法按预期执行所有操作,而且性能也稍差
@ywbaek 的解决方案比我的建议更安全,也更直观,但执行速度更快。
最简单的解决方案有一个小缺点,即不能用作 lambda。
我的时间代码:
N=10000
M=500
called = 0
def func(v):
global called
called += 1
v * v * v * v * v *v / (v+0.1)
def iterable(N):
for v in range(N):
v * 2
yield v
def testrun():
global called
called=0
print(timeit(test, number=M), end=" ")
print(called)
print("consume some CPU")
timeit(lambda: 3**.5 **.2) # it seems, that timeit is a little more predictable if I let the process warm up a little
print("Start measures")
def test():
for val in iterable(N): func(val)
testrun()
def test():
{None for val in iterable(N) if func(val)}
testrun()
def test():
[func(val) for val in iterable(N)]
testrun()
def test():
all(func(val) or True for val in iterable(N))
testrun()
def test():
any(func(val) and False for val in iterable(N))
testrun()
我的旧电脑上的结果:
consume some CPU
Start measures
3.864932143012993 5000000
3.916696268017404 5000000
4.0817033689818345 5000000
4.293206526956055 5000000
4.319622751965653 5000000