使用列表推导式来产生副作用是 Pythonic 吗?

发布于 2024-11-03 00:04:18 字数 440 浏览 1 评论 0原文

考虑一下我调用的函数是因为它的副作用,而不是返回值(例如打印到屏幕、更新 GUI、打印到文件等)。

def fun_with_side_effects(x):
    ...side effects...
    return y

现在,使用列表推导式来调用此函数是否符合 Python 风格:

[fun_with_side_effects(x) for x in y if (...conditions...)]

请注意,我不会将列表保存在任何地方

或者我应该这样调用此函数:

for x in y:
    if (...conditions...):
        fun_with_side_effects(x)

哪个更好为什么?

Think about a function that I'm calling for its side effects, not return values (like printing to screen, updating GUI, printing to a file, etc.).

def fun_with_side_effects(x):
    ...side effects...
    return y

Now, is it Pythonic to use list comprehensions to call this func:

[fun_with_side_effects(x) for x in y if (...conditions...)]

Note that I don't save the list anywhere

Or should I call this func like this:

for x in y:
    if (...conditions...):
        fun_with_side_effects(x)

Which is better and why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

扮仙女 2024-11-10 00:04:19

使用列表理解的副作用是丑陋的、非 Python 的、低效的,我不会这样做。我会使用 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 形式可能稍微更清晰一些,尽管我认为它们都令人困惑并且不应该使用。

我认为这很丑陋,我实际上不会用代码来做到这一点。但如果您坚持以这种方式实现循环,我就会这样做。

我倾向于认为列表推导式及其同类应该表明尝试使用至少有点类似于函数式风格的东西。放置具有破坏该假设的副作用的东西将导致人们必须更仔细地阅读您的代码,我认为这是一件坏事。

Using a list comprehension for its side effects is ugly, non-Pythonic, inefficient, and I wouldn't do it. I would use a for loop instead, because a for loop signals a procedural style in which side-effects are important.

But, if you absolutely insist on using a list comprehension for its side effects, you should avoid the inefficiency by using a generator expression instead. If you absolutely insist on this style, do one of these two:

any(fun_with_side_effects(x) and False for x in y if (...conditions...))

or:

all(fun_with_side_effects(x) or True for x in y if (...conditions...))

These are generator expressions, and they do not generate a random list that gets tossed out. I think the all form is perhaps slightly more clear, though I think both of them are confusing and shouldn't be used.

I think this is ugly and I wouldn't actually do it in code. But if you insist on implementing your loops in this fashion, that's how I would do it.

I tend to feel that list comprehensions and their ilk should signal an attempt to use something at least faintly resembling a functional style. Putting things with side effects that break that assumption will cause people to have to read your code more carefully, and I think that's a bad thing.

情栀口红 2024-11-10 00:04:18

这样做是非常反Python的,任何经验丰富的Pythonista都会让你受不了。中间列表在创建后就会被丢弃,并且它可能非常非常大,因此创建成本很高。

It is very anti-Pythonic to do so, and any seasoned Pythonista will give you hell over it. The intermediate list is thrown away after it is created, and it could potentially be very, very large, and therefore expensive to create.

忆离笙 2024-11-10 00:04:18

您不应该使用列表理解,因为正如人们所说,这将构建一个您不需要的大型临时列表。下面的两种方法是等价的:

consume(side_effects(x) for x in xs)

for x in xs:
    side_effects(x)

使用itertools手册页中的consume定义:

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)

当然,后者更清晰,更容易理解。

You shouldn't use a list comprehension, because as people have said that will build a large temporary list that you don't need. The following two methods are equivalent:

consume(side_effects(x) for x in xs)

for x in xs:
    side_effects(x)

with the definition of consume from the itertools man page:

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)

Of course, the latter is clearer and easier to understand.

呆萌少年 2024-11-10 00:04:18

列表推导式用于创建列表。除非您实际上正在创建列表,否则您不应该使用列表推导式。

所以我会选择第二个选项,只需迭代列表,然后在条件适用时调用该函数。

List comprehensions are for creating lists. And unless you are actually creating a list, you should not use list comprehensions.

So I would got for the second option, just iterating over the list and then call the function when the conditions apply.

恰似旧人归 2024-11-10 00:04:18

第二个更好。

想想需要理解您的代码的人。第一个很容易造成不好的业力:)你

可以使用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)

Second is better.

Think of the person who would need to understand your code. You can get bad karma easily with the first :)

You could go middle between the two by using filter(). Consider the example:

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)
疾风者 2024-11-10 00:04:18

取决于你的目标。

如果您尝试对列表中的每个对象执行某些操作,则应采用第二种方法。

如果您尝试从另一个列表生成列表,则可以使用列表理解。

显式优于隐式。
简单总比复杂好。 (Python 禅)

Depends on your goal.

If you are trying to do some operation on each object in a list, the second approach should be adopted.

If you are trying to generate a list from another list, you may use list comprehension.

Explicit is better than implicit.
Simple is better than complex. (Python Zen)

寒尘 2024-11-10 00:04:18

你可以这样做

for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass

,但它不是很漂亮。

You can do

for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass

but it's not very pretty.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文