如何从列表末尾删除 None 的所有实例?

How to remove all instances of None from the end of a list?

Python 有一个名为 rstrip():

的字符串方法
>>> s = "hello world!!!"
>>> s.rstrip("!")
'hello world'

我想为 Python 列表实现类似的功能。也就是说,我想从列表末尾删除给定值的所有实例。在本例中,值为 None.

这里有一些开始的例子:

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

我希望最终结果是:

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

到目前为止,这是我的解决方案:

while l[-1] is None:
    l.pop()

如果你想修改列表in-place,那么你的解决方案很好,只要确保处理列表为空的情况即可:

while l and l[-1] is None:
    l.pop()

如果您想计算一个新列表,您可以将您的解决方案调整为:

def stripNone(l):
    if not l:
        return []
    
    rlim = 0
    for x in reversed(l):
        if x is None:
            rlim += 1
        else:
            break
    
    return l[: len(l) - rlim]

还有itertools.dropwhile,但是你要进行两次反转:

def stripNone(l):
    return list(dropwhile(lambda x: x is None, l[::-1]))[::-1]

另外两个版本也适用于 None-only 列表:

while None in l[-1:]:
    l.pop()
for x in reversed(l):
    if x is not None:
        break
    l.pop()

l = [None] * 10**3 上对一些解决方案进行基准测试:

 83 us  stripNone1
137 us  stripNone2
 60 us  stripNone3
 42 us  stripNone3b
 53 us  stripNone4
 34 us  stripNone5
 19 us  stripNone6

请注意 stripNone2stripNone6 有一个小缺陷:如果在跟踪 None 中有一个对象不是 None 但声称 equal None,那么它将被删除。不过,这样的物体非常不寻常。也许有人实际上 想要 删除这样的对象。

基准代码:

def stripNone1(l):
    while l and l[-1] is None:
        l.pop()

def stripNone2(l):
    while None in l[-1:]:
        l.pop()

def stripNone3(l):
    for x in reversed(l):
        if x is not None:
            break
        l.pop()

def stripNone3b(l):
    pop = l.pop
    for x in reversed(l):
        if x is not None:
            break
        pop()

def stripNone4(l):
    for i, x in enumerate(reversed(l), 1):
        if x is not None:
            del l[-i:]
            break

def stripNone5(l):
    pop = l.pop
    try:
        while (last := pop()) is None:
            pass
        l.append(last)
    except IndexError:
        pass

def stripNone6(l):
    while l:
        chunk = l[-32:]
        if chunk.count(None) < len(chunk):
            while l[-1] is None:
                l.pop()
            break
        del l[-32:]

from timeit import repeat
solutions = stripNone1, stripNone2, stripNone3, stripNone3b, stripNone4, stripNone5, stripNone6
for i in range(3):
    print(f'Round {i+1}:')
    for sol in solutions:
        ls = [[42] * head + [None] * tail
              for _ in range(5)
              for head in range(0, 2001, 200)
              for tail in range(0, 2001, 200)]
        number = len(ls) // 5
        ls = iter(ls)
        time = min(repeat(lambda: sol(next(ls)), number=number)) / number
        print(f'{int(time * 10**6):3d} us  {sol.__name__}')