如何检查列表中的所有元素是否都符合条件?


208

我有一个包含20000个列表的列表。我将每个列表的第3个元素用作标志。只要至少一个元素的标志为0,我想在此列表上执行一些操作,就像:

my_list = [["a", "b", 0], ["c", "d", 0], ["e", "f", 0], .....]

开始时,所有标志均为0。我使用while循环检查至少一个元素的标志是否为0:

def check(list_):
    for item in list_:
        if item[2] == 0:
            return True
    return False

如果check(my_list)返回True,那么我将继续处理我的列表:

while check(my_list):
    for item in my_list:
        if condition:
            item[2] = 1
        else:
            do_sth()

实际上,我想在对my_list进行迭代时删除其中的一个元素,但是在对它进行迭代时,不允许删除项目。

原始的my_list没有标志:

my_list = [["a", "b"], ["c", "d"], ["e", "f"], .....]

由于在迭代过程中无法删除元素,因此发明了这些标志。但是,my_list其中包含许多项目,并且while循环在每个for循环中读取所有项目,这会花费大量时间!你有什么建议吗?


3
看起来您的数据结构不适合您的问题。如果您对上下文有更多的解释,也许我们可以建议一些更合适的方法。
uselpa 2012年

也许您可以用None[]在列表中进行迭代时替换这些项目,而不是删除它们。在每次通过内部循环之前,使用'check()`遍历所有项来检查整个列表是一种非常缓慢的方法。
martineau 2012年

Answers:


402

最好的答案是使用all(),这是这种情况的内置函数。我们将其与生成器表达式结合使用,以干净高效地生成您想要的结果。例如:

>>> items = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
True
>>> items = [[1, 2, 0], [1, 2, 1], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
False

请注意,all(flag == 0 for (_, _, flag) in items)它直接等效于all(item[2] == 0 for item in items),在这种情况下阅读起来要好一些。

并且,对于过滤器示例,使用列表推导(当然,可以在适当的地方使用生成器表达式):

>>> [x for x in items if x[2] == 0]
[[1, 2, 0], [1, 2, 0]]

如果要检查至少一个元素为0,则更好的选择是使用any()更具可读性的元素:

>>> any(flag == 0 for (_, _, flag) in items)
True

我对使用lambda的错是,Python的全部都不接受函数作为第一个参数,例如Haskell等。等,我也更改了对列表理解的答案。:)
汉普斯·尼尔森

3
@HampusNilsson列表理解与生成器表达式不同。作为all()any()短路,如果,例如,在矿井的第一个值的计算结果为Falseall()将失败,并且不检查任何更多的值,返回False。您的示例将执行相同的操作,不同之处在于它将首先生成比较的整个列表,这意味着大量的处理工作。
加雷斯·拉蒂

14

如果要检查列表中的任何项目是否违反条件,请使用all

if all([x[2] == 0 for x in lista]):
    # Will run if all elements in the list has x[2] = 0 (use not to invert if necessary)

要删除所有不匹配的元素,请使用 filter

# Will remove all elements where x[2] is 0
listb = filter(lambda x: x[2] != 0, listb)

2
您可以删除它[...]all(...)因为它可以创建一个生成器而不是一个列表,这不仅可以节省两个字符,还可以节省内存和时间。通过使用生成器,一次将仅计算一项(由于不再使用以前的结果将被丢弃),并且如果其中任何一项出现False,生成器将停止计算其余项。
InQβ

7

您可以像这样使用itertools的takewhile,一旦满足条件会使您的语句失败,它将停止。相反的方法是dropwhile

for x in itertools.takewhile(lambda x: x[2] == 0, list)
    print x

0

另一种使用方式itertools.ifilter。这会检查真实性和过程(使用lambda

样品-

for x in itertools.ifilter(lambda x: x[2] == 0, my_list):
    print x

0

这种方式比使用以下方式更加灵活all()

my_list = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
all_zeros = False if False in [x[2] == 0 for x in my_list] else True
any_zeros = True if True in [x[2] == 0 for x in my_list] else False

或更简洁地:

all_zeros = not False in [x[2] == 0 for x in my_list]
any_zeros = 0 in [x[2] for x in my_list]

您不能简单地说一下all_zeros = False in [x[2] == 0 for x in my_list],甚至0 in [x[2] for x in my_list]相应地any_zeros吗?我确实看不到的任何明显改进all()
Tripleee'7

不,您的版本- all_zeros = False in [x[2] == 0 for x in my_list]评估为False,而我的版本为True。如果将其更改为all_zeros = not (False in [x[2] == 0 for x in my_list]),则等同于我的。而且0 in [x[2] for x in my_list]显然只是为了工作any_zeros。但我确实喜欢您的想法简洁明了,所以我将更新我的答案
mulllhausen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.