如何用以前的值替换列表中的 None

How to replace None in the List with previous value

我想用前面的变量替换 list 中的 None(对于所有连续的 None)。我用 iffor (多行)做到了。有没有办法在一行中做到这一点?即,列表理解、Lambda 和/或 map

我的想法是使用列表理解,但我无法在列表理解中分配变量来设置先前的值。

我的项目中有一个类似的场景以这种方式处理 None,问题是我不想为小功能编写 10 行代码。

def none_replace(ls):
    ret = []
    prev_val = None
    for i in ls:
        if i:
            prev_val = i
            ret.append(i)
        else:
            ret.append(prev_val)
    return ret

print('Replaced None List:', none_replace([None, None, 1, 2, None, None, 3, 4, None, 5, None, None]))

输出:

Replaced None List: [None, None, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5]

在 Python 3.8 或更高版本中,您可以使用 assignment operator:

def none_replace(ls):
    p = None
    return [p:=e if e is not None else p for e in ls]

您可以利用列表的可变性

x =[None, None, 1, 2, None, None, 3, 4, None, 5, None, None]
for i,e in enumerate(x[:-1], 1):
    if x[i] is None:
        x[i] = x[i-1]
print(x)

输出

[None, None, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5]

Is there any way to do this in a single line? i.e., List comprehension, Lambda and or map

我不这么认为,因为 the late binding closures。此外,它可能无法读取。

I have got a similar scenario in my project to handle None in such a way, the thing is I don't want to write 10 lines of code for the small functionality.

为什么你觉得它小?问题是需要解决的问题。一个小函数听起来很适合解决它。

我的解决方法:

def none_replace(ls: list):
    prev, this = ls[0], None
    assert prev is not None, "First arg can't be None"

    for i in range(1, len(ls)):
        this = ls[i]
        if this is None:
            ls[i] = prev
        prev = this or ls[i]

    return ls

print('Replaced None List:', none_replace(['asd', None, None, 1, 2, None, None, 3, 4, None, 5, None, None]))

为了完整起见,one-line 版本 Python < 3.8:

[
    x.__setitem__(i, e if e is not None else x[i-1] if i > 0 else None)
    or x[i]
    for i, e in enumerate(x)
]

我不得不将它分成几行以便在此处显示可能已经表明这是非常不可读的并且不应该使用。


list.__setitem__ returns None,所以我使用 or 总是 return 当前在该位置列表中的任何内容(or x[i]).作为 side-effect,如果 i-th 元素不是 None,则 list.__setitem__ 将其设置为自身,否则如果 i 不为 0,则设置为之前的值,否则 None.

您可以使用函数 accumulate() 和运算符 or:

from itertools import accumulate

list(accumulate(lst, lambda x, y: y or x))
# [None, None, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5]

在此解决方案中,您采用元素 y 和前一个元素 x 并使用运算符 or 比较它们。如果 yNone 你取前一个元素 x;否则,你拿y。如果两者都是 None 你会得到 None.

类似于我们朋友的解决方案之一。为了便于阅读,略作修改。使用 range(len())

而不是枚举
x =[None, None, 1, 2, None, None, 3, 4, None, 5, None, None]
for i in range(len(x)):
   if x[i] is None:
     x[i] = x[i-1]
print(x) 

输出: [None, None, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5]

为第一个值x[-1]到None的值。所以执行程序时没有错误...

我们非常感谢您的反馈。:)

您可以使用 生成器 来生成适当的值。简而言之,生成器是生成值序列并保持其最后状态的函数。当调用生成器时,它们将从上次调用继续。我们利用这个特性来访问列表中最后访问的项目。请看以下代码:

L=[None, None, 1, 2, None, None, 3, 4, None, 5, None, None]

def replaceNoneGenerator(L):
    prev_val=None;
    for item in L:
        prev_val=prev_val if item is None else item
        yield prev_val

L1=[item for item in replaceNoneGenerator(L)]

print(L1)

输出[None, None, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5]

for i in range(len(ls)):
   if ls[i] is None:
     ls[i] = ls[i-1]

如果它是 None,则将其设置为前一个变量。

a = [1,2, None, 3,4, None]
print([a[x] if a[x] else a[x-1]  for x in range(len(a))])

这绝对忽略了 None 出现在列表第一位的情况,但随后处理它也不应该是一项艰巨的任务。