如何在Python中删除列表中的所有None元素
我想从这个列表中获取唯一一个不是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 个回答
为了好玩,我们可以这样做:
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()
的设计目的是将一个包含多个 list
的 list
展开,以便进行遍历。ifilterfalse
可以直接作用于返回的 chain
。ifilterfalse
会去掉那些符合 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 愉快!
这里有一种稍微更模块化的方法:
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
函数是你应该在工具箱里保留的,因为到目前为止,它在标准库里找不到,而且偶尔会用到。
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))
,但在大多数情况下你并不需要这样做。
这样解释清楚了吗?