【问题标题】:Why strange list comprehension behavior with side effects?为什么奇怪的列表理解行为有副作用?
【发布时间】:2012-10-16 23:16:43
【问题描述】:

我知道在 Python 列表推导中使用副作用不是一个好习惯。但我不明白为什么会发生以下情况:

In [66]: tmp = [1,2,3,4,5]; [tmp.remove(elem) for elem in tmp]
Out[66]: [None, None, None]

In [67]: tmp
Out[67]: [2, 4]

无论这是否是好的做法,列表理解的内部不应该做一些可预测的事情吗?如果上述是可预测的,有人可以解释为什么只发生了三个remove 操作,为什么偶数条目是剩下的?

【问题讨论】:

  • @JoshLee 我不需要删除元素本身,我只是想了解为什么 None or... 的东西不起作用。事实证明它正在工作,我没有意识到索引正在改变。
  • 另外,在这个例子中,我放置None的部分实际上是一个昂贵的计算,可能导致返回None,在这种情况下从tmp中删除项目发生这种情况的地方可能对我来说是一个可行的选择。但无论哪种方式,在列表解析的末尾也包含一个 if 子句并不好,因为它需要再次评估那个昂贵的函数才能知道要保留什么。
  • 好吧,你总是可以把None or x改写成x。
  • None 可能由我的实际代码中的昂贵函数返回。

标签: python lambda list-comprehension side-effects


【解决方案1】:

这不是关于 listcomps,而是关于从您正在迭代的列表中删除:

>>> tmp = [1,2,3,4,5]
>>> for elem in tmp:
...     tmp.remove(elem)
... 
>>> tmp
[2, 4]

它是这样的:

>>> tmp = [1,2,3,4,5]
>>> for elem in tmp:
...     print elem, tmp
...     tmp.remove(elem)
...     print elem, tmp
... 
1 [1, 2, 3, 4, 5]
1 [2, 3, 4, 5]
3 [2, 3, 4, 5]
3 [2, 4, 5]
5 [2, 4, 5]
5 [2, 4]

首先它查看第 0 个元素,然后删除 1。所以在下一次迭代中,它想要删除第一个元素,现在是第 3 个,等等。

【讨论】:

  • 知道了,所以如果我做到[None or tmp.remove(elem) for elem in list(tmp)] 或[None or tmp.remove(elem) for elem in tmp[:]] 那么它应该“按预期”工作(仍然是糟糕的风格)。
【解决方案2】:

当您直接迭代列表时,从列表中删除绝不是一个好主意。 编辑:哎呀,我把列表和字典混淆了。字典有一个 iteritems() 和 iterkeys() 方法,当您迭代字典并从中删除项目时,您将使用它们。

对于列表,如果您想在列表组合中执行此操作,您可能需要复制该列表。或者,交替:

[None and tmp.pop(0) for i in xrange(len(tmp))]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-26
    • 2015-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-14
    • 2013-12-28
    相关资源
    最近更新 更多