问题:使用列表推导只是副作用是Pythonic吗?
考虑一下我需要它带来副作用的函数,而不是返回值(例如打印到屏幕,更新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
这样做是非常反Python的,任何经验丰富的Pythonista都会为您带来麻烦。中间列表在创建之后就被丢弃了,它可能非常大,因此创建起来很昂贵。
回答 1
您不应该使用列表理解,因为正如人们所说的那样,这将建立您不需要的大型临时列表。以下两种方法是等效的:
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
列表推导用于创建列表。并且,除非您实际创建列表,否则不应使用列表推导。
因此,我将选择第二个选项,即遍历列表,然后在条件适用时调用该函数。
回答 3
第二更好。
想想需要了解您的代码的人。您可以通过第一个方法轻松获得不良业障:)
您可以使用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
取决于您的目标。
如果尝试对列表中的每个对象执行某些操作,则应采用第二种方法。
如果您尝试从另一个列表生成列表,则可以使用列表理解。
显式胜于隐式。简单胜于复杂。(Python Zen)
回答 5
你可以做
for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass
但是不是很漂亮
回答 6
使用列表理解来产生副作用是丑陋的,非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
表格可能会稍微清晰些,尽管我认为两者都令人困惑并且不应该使用。
我认为这很丑陋,实际上我不会在代码中这样做。但是,如果您坚持以这种方式实现循环,那就是我要这样做的方式。
我倾向于认为列表理解和它们的同类应该表明尝试使用至少略带功能性风格的东西。放置带有破坏该假设的副作用的东西将导致人们不得不更仔细地阅读您的代码,我认为这是一件坏事。