Is it Pythonic to use list comprehensions for just side effects?
想想我调用的函数的副作用,而不是返回值(比如打印到屏幕、更新GUI、打印到文件等)。
1 2 3 | def fun_with_side_effects(x): ...side effects... return y |
现在,用列表理解来称呼这个func是不是pythonic?
1 | [fun_with_side_effects(x) for x in y if (...conditions...)] |
请注意,我不会在任何地方保存列表
或者我应该这样称呼这个func:
1 2 3 | for x in y: if (...conditions...): fun_with_side_effects(x) |
哪个更好,为什么?
这样做是非常反Python,任何经验丰富的Python都会给你地狱。中间列表在创建后被丢弃,它可能非常、非常大,因此创建成本很高。
你不应该使用列表理解,因为正如人们所说,这将构建一个你不需要的大型临时列表。以下两种方法是等效的:
1 2 3 4 | consume(side_effects(x) for x in xs) for x in xs: side_effects(x) |
根据
1 2 3 4 5 6 7 8 9 | 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) |
当然,后者更清晰,更容易理解。
列表理解用于创建列表。除非你真的在创建一个列表,否则你不应该使用列表理解。
所以我得到了第二个选项,只是遍历列表,然后在条件适用时调用函数。
第二个更好。
想想那些需要理解您的代码的人。你可以很容易地得到坏业力与第一个:)
您可以使用filter()在两者之间进行中间处理。举个例子:
1 2 3 4 5 6 | 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) |
取决于你的目标。
如果您试图对列表中的每个对象执行一些操作,则应该采用第二种方法。
如果您试图从另一个列表生成一个列表,则可以使用列表理解。
Explicit is better than implicit.
Simple is better than complex. (Python Zen)
你可以做到
1 | for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass |
但不是很漂亮。
使用清单理解它的副作用是丑陋的,非Python,效率低下,我不会这样做。我将使用一个
但是,如果您绝对坚持使用列表理解来解决其副作用,那么应该使用生成器表达式来避免效率低下。如果您绝对坚持这种风格,请执行以下两种操作之一:
1 | any(fun_with_side_effects(x) and False for x in y if (...conditions...)) |
或:
1 | all(fun_with_side_effects(x) or True for x in y if (...conditions...)) |
这些是生成器表达式,它们不会生成被抛出的随机列表。我认为
我觉得这很难看,实际上我不会用代码来做。但如果您坚持以这种方式实现循环,我会这样做的。
我倾向于认为列表理解和它们的ILK应该表示尝试使用某种至少有点类似于功能风格的东西。把有副作用的东西放在违背这个假设的地方会让人们更仔细地阅读你的代码,我认为这是一件坏事。