如何在Python中删除列表中的所有None元素

1 投票
3 回答
1988 浏览
提问于 2025-04-17 23:04

我想从这个列表中获取唯一一个不是None的元素:

L = [None, [None,None], [None, <__main__.Car object at 0x02A11550>], [None, None, None], None]

我试过这样做:

L = [x for x in L if x is not None]

但是结果是:

[[None, None], [None, <__main__.Car object at 0x02A11550>], [None, None, None]]

我只想删除那些不在任何列表里的None。有没有办法清理整个列表?这样输出的结果是:

<__main__.Car object at 0x02A11550>

3 个回答

0

为了好玩,我们可以这样做:

from itertools import chain, ifilterfalse
result = list(ifilterfalse(lambda x: x is None, chain(*[x for x in L if x is not None])))

这样做会返回一个只包含 Car 元素的 list。它可以扩展到返回任何非 None 元素的 list

在 Python 3.x 中,我认为你可以把 ifilterfalse 替换成 filterfalse,效果是一样的。

chain() 的设计目的是将一个包含多个 listlist 展开,以便进行遍历。ifilterfalse 可以直接作用于返回的 chainifilterfalse 会去掉那些符合 lambda 函数指定条件的元素。

需要注意的是,如果你的 L 中有字符串,chain() 会把这些字符串拆分成单个字符。如果这对你来说是个问题,可以看看这个其他的 SO帖子

还有一种实现方式,可以避免在最底层出现非可迭代的情况:

result = list(ifilterfalse(lambda x: x is None, chain(*[x if hasattr(x, '__iter__') else [x] for x in L if x is not None])))

我听说这在 Python 3 中可能不太好用,因为 str 在那里是这样实现的。无论如何,我只是分享这些想法,让你知道在 Python 标准库的 itertools 中已经有的功能。祝你学习 Python 愉快!

1

这里有一种稍微更模块化的方法:

def flatten(l):
    """ http://stackoverflow.com/questions/2158395/flatten-an-irregular-list-of-lists-in-python/2158532#2158532 """
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, basestring):
            for sub in flatten(el):
                yield sub
        else:
            yield el

filter(None,flatten(L)) #wrap with `list` in python 3.x

一个通用的 flatten 函数是你应该在工具箱里保留的,因为到目前为止,它在标准库里找不到,而且偶尔会用到。

3
def flatten(lst):
    for element in lst:
        if hasattr(element,"__iter__"):
            yield from flatten(element)
        elif not element is None:
            yield element

new_list = flatten(L)

我来给你简单解释一下,先从生成器说起。yield这个关键词和return是姐妹关系,但功能大不相同。它们都用来把函数里的值带到调用它的地方,但yield允许你在之后再跳回这个函数!举个例子,下面这个生成器接受一个满是数字的列表,然后为列表中的每个数字计算平方。

def example_generator(number_list):
    for number in number_list:
        yield number**2

>>> gen = example_generator([1,2,3])
>>> type(gen)
<class 'generator'>
>>> next(gen) # next() is used to get the next value from an iterator
1
>>> next(gen)
4
>>> next(gen)
9
>>> next(gen)
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    next(gen)
StopIteration

不过,生成器只能用一次。你看,当我到达生成器的末尾时,它会抛出一个异常StopIteration。如果我再重新构建它,并用循环运行一遍,然后再试着再运行一次……

>>> gen = example_generator([1,2,3]) # remember this is a new generator, we JUST made it
>>> for item in gen:
...     print(item)
1
4
9
>>> for item in gen:
...     print(item)
>>>

第二次就什么都不做了。生成器已经用完了。这是个缺点——但好处是,使用生成器通常比使用列表要快得多,并且更节省内存。

yield还允许你使用另一个关键词:from。我在处理嵌套列表时就是这么做的(hasattr(element,"__iter__")的意思是这个元素有一个属性.__iter__,这意味着它可以用类似for循环的方式进行迭代)。你给yield from另一个生成器,它就会依次输出那个生成器中的每个元素。例如:

def flatten_lite(lst):
    for element in lst:
        if type(element) is list: # more readable, IMO
            yield from flatten_lite(element)
        else:
            yield element

a = flatten_lite([1,2,3,[4,5,6,[7],8],9])

这段代码的作用是:

for element in [1,2,3,[4,5,6,[7],8],9]:
    # element == 1
    if element is of type list: # it's not, skip this
    else: yield element # which is 1
    :: NEXT ITERATION ::
    # element == 2, same as before
    :: NEXT ITERATION ::
    # element == 3, same as before
    :: NEXT ITERATION ::
    # element == [4,5,6,[7],8]
    if element is of type list: # it is!!
        yield from flatten_lite([4,5,6,[7],8])
        :: STOP EXECUTION UNTIL WE GET A VALUE FROM THAT NEW GENERATOR ::
>>> NEW GENERATOR
for element in [4,5,6,[7],8]:
    # element is 4
    yield 4
        :: THE OUTER GENERATOR YIELDS 4 ::
    :: NEXT ITERATION ::
    # element is 5
    yield 5
        :: THE OUTER GENERATOR YIELDS 4 ::
    :: NEXT ITERATION ::
    # element is 6
    yield 6
        :: THE OUTER GENERATOR YIELDS 4 ::
    :: NEXT ITERATION ::
    # element is [7]
    if element is of type list # [7] is a list!
        yield from flatten_lite([7])
            :: STOP EXECUTION UNTIL WE GET A VALUE FROM THAT NEW GENERATOR ::
            # etc etc

所以基本上,上面的代码可以用伪代码这样理解:

flatten is a function that accepts parameter: lst
    for each element in lst:
        if element can be iterated on:
            yield every element in turn from the generator created
              by this function called on the element instead of the
              main list
        if it's not, and isn't None:
            yield element

当你调用它时,它会构建一个可以迭代的生成器。要把它变成一个正式的列表,你需要用list(flatten(L)),但在大多数情况下你并不需要这样做。

这样解释清楚了吗?

撰写回答