考虑一个函数,我要求它的副作用,而不是返回值(如打印到屏幕,更新GUI,打印到文件等)。
def fun_with_side_effects(x):
...side effects...
return y
现在,用 Pythonic 来使用列表推导来调用这个函数:
[fun_with_side_effects(x) for x in y if (...conditions...)]
请注意,我不会将列表保存在任何地方
或者我应该像这样调用这个函数:
for x in y:
if (...conditions...):
fun_with_side_effects(x)
哪个更好,为什么?
答案 0 :(得分:72)
这样做非常反Pythonic,而且任何经验丰富的Pythonista都会让你诅咒它。中间列表在创建后会被丢弃,并且可能非常非常大,因此创建起来很昂贵。
答案 1 :(得分:28)
您不应该使用列表理解,因为正如人们所说,将构建一个您不需要的大型临时列表。以下两种方法是等效的:
consume(side_effects(x) for x in xs)
for x in xs:
side_effects(x)
使用consume
手册页中的itertools
定义:
def consume(iterator, n=None):
"Advance the iterator n-steps ahead. If n is none, consume entirely."
# Use functions that consume iterators at C speed.
if n is None:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
else:
# advance to the empty slice starting at position n
next(islice(iterator, n, n), None)
当然,后者更清晰,更容易理解。
答案 2 :(得分:20)
列表推导用于创建列表。除非您实际创建列表,否则不使用列表推导。
所以我想得到第二个选项,只是迭代列表,然后在条件适用时调用函数。
答案 3 :(得分:10)
其次更好。
想想需要理解代码的人。你可以通过第一个轻松获得恶误的业力:)
你可以使用filter()在两者之间中间。考虑一下这个例子:
y=[1,2,3,4,5,6]
def func(x):
print "call with %r"%x
for x in filter(lambda x: x>3, y):
func(x)
答案 4 :(得分:3)
取决于你的目标。
如果您尝试对列表中的每个对象执行某些操作,则应采用第二种方法。
如果您尝试从其他列表生成列表,则可以使用列表推导。
明确比隐含更好。 简单比复杂更好。 (Python Zen)
答案 5 :(得分:1)
你可以做到
for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass
但它不是很漂亮。
答案 6 :(得分:-1)
使用列表理解它的副作用是丑陋的,非Pythonic,低效,我不会这样做。我会使用for
循环,因为for
循环表示副作用很重要的程序样式。
但是,如果您绝对坚持使用列表推导来解决其副作用,则应该通过使用生成器表达式来避免效率低下。如果您绝对坚持这种风格,请执行以下两种方法之一:
any(fun_with_side_effects(x) and False for x in y if (...conditions...))
或:
all(fun_with_side_effects(x) or True for x in y if (...conditions...))
这些是生成器表达式,它们不会生成被抛出的随机列表。我认为all
形式可能稍微清楚一点,但我认为它们都令人困惑,不应该使用。
我认为这很难看,我实际上不会在代码中这样做。但如果你坚持以这种方式实现你的循环,我就是这样做的。
我倾向于认为列表理解及其同类应该标志着尝试使用至少微弱地类似于功能风格的东西。将具有破坏该假设的副作用放在一起会导致人们不得不更仔细地阅读您的代码,我认为这是件坏事。