Python的列表理解:如果出现某个值,则修改列表元素

时间:2018-09-19 21:34:54

标签: python list-comprehension

如何在Python的列表理解中执行以下操作?

nums = [1,1,0,1,1]
oFlag = 1
res = []
for x in nums:
    if x == 0:
        oFlag = 0
    res.append(oFlag)
print(res)

# Output: [1,1,0,0,0]

本质上,在此示例中,一旦发生0,将列表的其余部分清零。

5 个答案:

答案 0 :(得分:8)

在某些情况下,列表推导是许多功能编程语言中存在的mapfilter函数的一种“命令式”语法。您尝试执行的操作通常称为accumulate,该操作稍有不同。除了使用副作用之外,不能根据accumulatemap来实现filter。 Python允许您在列表理解中具有副作用,因此这是绝对可能的,但是具有副作用的列表理解却有点奇怪。这是使用累计来实现的方法:

nums = [1,1,0,1,1]

def accumulator(last, cur):
    return 1 if (last == 1 and cur == 1) else 0

list(accumulate(nums, accumulator))

或一行:

list(accumulate(nums, lambda last, cur: 1 if (last == 1 and cur == 1) else 0))

当然,有几种方法可以使用外部状态和具有副作用的列表理解功能。这是一个示例,它有点冗长,但是非常明确地说明了状态的操作方式:

class MyState:
    def __init__(self, initial_state):
        self.state = initial_state
    def getNext(self, cur):
        self.state = accumulator(self.state, cur)
        return self.state

mystate = MyState(1)
[mystate.getNext(x) for x in nums]

答案 1 :(得分:6)

nums = [1,1,0,1,1]
[int(all(nums[:i+1])) for i in range(len(nums))]

这会逐步遍历列表,直到此时将all运算符应用于整个子列表。

输出:

[1, 1, 0, 0, 0]

可以,这是 O(n ^ 2),但是可以完成工作。

更有效的方法是简单地找到前0个索引。 新建一个包含这么多1的列表,并填充适当数量的零。

if 0 in nums:
    idx = nums.index(0)
    new_list = [1] * idx + [0] * (len(nums) - idx)

...,或者如果原始列表可以包含0和1以外的元素,请将列表复制到尽可能远的位置,而不要重复1

    new_list = nums[:idx] + [0] * (len(nums) - idx)

答案 2 :(得分:5)

我有一个使用列表理解的答案,但是@Prune击败了我。这实际上只是一条警告尾巴,显示了反对该方法的论点时该如何做。

这是一种可能满足您需求的替代方法:

import itertools
import operator

nums = [1,1,0,1,1]
res = itertools.accumulate(nums, operator.and_)

在这种情况下,res是可迭代的。如果您需要列表,那么

res = list(itertools.accumulate(nums, operator.and_))

让我们分解一下。 accumulate()函数可用于生成运行总计或“累计总和”。如果仅传递一个参数,则默认功能为加法。在这里,我们传入operator.and_。 operator模块导出一组与Python的内在运算符相对应的有效函数。当累积的and在0和1的列表上运行时,结果是列表中的1一直到找到第一个0,然后是所有0。

当然,我们不仅限于使用operator模块中定义的函数。您可以使用任何在第一个参数中接受两个元素类型的参数的函数(并且可能返回相同的类型)。您可以发挥创造力,但在这里我将使其保持简单,仅实现and

import itertools

nums = [1,1,0,1,1]
res = itertools.accumulate(nums, lambda a, b: a and b)

注意:使用operator.and_可能运行得更快。在这里,我们只是提供一个使用lambda语法的示例。

虽然没有使用列表理解,但对我来说却有类似的感觉。它合而为一,读起来也不难。

答案 3 :(得分:3)

对于列表理解方法,可以将indexenumerate结合使用:

firstIndex = nums.index(0) if 0 in nums else -1
[1 if i < firstIndex else 0 for i, x in enumerate(nums)]

使用numpy的另一种方法:

import numpy as np
print(np.cumprod(np.array(nums) != 0).tolist())
#[1, 1, 0, 0, 0]

在这里,我们将转换nums转换为一个numpy数组,并检查值是否不等于0。然后取该数组的累加乘积,知道一旦找到0,我们将乘以从那以后向前减0。

答案 4 :(得分:1)

这是一个线性时间解决方案,它不改变全局状态,不需要nums以外的任何其他迭代器,并且可以满足您的要求,尽管需要一些辅助数据结构并使用严重黑名单的理解力:

>>> nums = [1,1,0,1,1]
>>> [f for f, ns in [(1, nums)] for n in ns for f in [f & (n==1)]]
[1, 1, 0, 0, 0]

请勿使用。使用原始的for循环。它更具可读性,并且几乎可以肯定更快。不要努力将所有内容放在列表理解中。力求使您的代码简单,易读和可维护,而您的代码已经是,而上面的代码则不是。