如何从Python中的列表列表中删除所有无元素

时间:2014-03-20 00:56:54

标签: python list nonetype

我想从此列表中获取唯一的Non-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 :(得分: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另一个生成器,它依次从THAT生成器中生成每个元素。例如:

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)),但在大多数情况下,您不需要这样做。

有没有更明确的?

答案 1 :(得分: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功能是您应该保留在工具箱中的功能,因为(到目前为止)它不是您可以在标准库中找到的东西,偶尔会出现。

答案 2 :(得分: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])))

这将返回仅包含list元素的Car。它会推广使用任何非list元素返回None

在Python 3.x中,我认为您将ifilterfalse换成filterfalse,它的工作方式相同。

chain()旨在展开list list次迭代。 ifilterfalse可以直接在chain返回。 ifilterfalse删除与lambda函数指定的谓词匹配的元素。

请注意,如果L中有字符串,chain()基本上会将字符串分解为单个元素。如果这对您来说是个问题,请参阅此其他SO post

另一种避免基本级别不可迭代问题的实现:

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在那里实现的方式。无论如何,我只发布这些想法,以便您了解itertools下Python标准库中已有的功能。玩得开心学习Python!