如何检查列表的所有元素是否与条件匹配?

How to check if all elements of a list match a condition?

提问人:alwbtc 提问时间:5/19/2012 最后编辑:Karl Knechtelalwbtc 更新时间:10/18/2023 访问量:511948

问:

我有一个列表,其中包含许多子列表,每个子列表有 3 个元素,例如:

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

每个子列表的最后一个元素是一种标志,对于每个子列表,它最初为 0。随着我的算法的进展,我想检查这个标志是否至少一个元素为 0。目前我使用while循环,如下所示:

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

只要满足该条件,整个算法就会循环,并在每次迭代中设置一些标志:

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

因为在迭代列表时从列表中删除元素会导致问题,所以我使用这些标志来跟踪已经处理的元素。

如何简化或加快代码速度?


另请参阅检查列表的任何元素的条件是否成立的 Python 方法,以检查任何元素的条件。请记住,“任何”和“所有”检查通过德摩根定律是相关的,就像“或”和“和”是相关的一样。

此处的现有答案都使用内置函数进行迭代。请参阅 Python 的 any 和 all 函数如何工作? 了解 all 及其对应项 any 的解释。

如果要检查的条件是“在另一个容器中找到”,请参阅如何检查以下所有项目是否都在列表中?及其对应项,如何检查以下项目之一是否在列表中?。使用任何所有方法都可以使用,但更有效的解决方案是可能的。

python 列表 for-loop while 循环

评论

4赞 uselpa 5/19/2012
看起来您的数据结构不适合您的问题。如果你对上下文多解释一点,也许我们可以提出更合适的建议。
0赞 martineau 5/20/2012
也许您可以在循环访问列表时将项目替换为 or 而不是删除它们。在每次传递内部循环之前,使用'check()'遍历所有项目来检查整个列表是一种非常缓慢的方法。None[]
0赞 Karl Knechtel 10/18/2023
我编辑了这个问题,试图让它更容易阅读并满足当前的风格指南;但现在看来,它最初是由XY问题引起的。正确过滤列表会更好(并且实际上解决了所引用的性能问题),而不是寻找“更好”的方法来处理标志。

答:

8赞 Hedde van der Heide 5/19/2012 #1

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

for x in itertools.takewhile(lambda x: x[2] == 0, list)
    print x
28赞 Hampus Nilsson 5/19/2012 #2

如果要检查列表中是否有任何项目违反条件,请使用: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)

评论

5赞 InQβ 5/22/2018
您可以删除它,因为它可以创建一个生成器而不是列表,这不仅可以节省两个字符,还可以节省内存和时间。通过使用生成器,一次只会计算一个项目(以前的结果将被丢弃,因为不再使用),如果其中任何一个结果出现,生成器将停止计算其余的项目。[...]all(...)False
1赞 Meg Anderson 8/11/2020
python3 注意事项; 返回一个可迭代对象,而不是一个列表。因此,该行将是filter()listb = list(filter(lamba x: x[2] != 0, listb))
547赞 Gareth Latty 5/19/2012 #3

这里最好的答案是使用 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

评论

0赞 Hampus Nilsson 5/19/2012
我在使用 lambda 方面的错误,Python 的 all 不接受像 Haskell et 这样的函数作为第一个参数。al.,我也把我的答案改成了列表理解。:)
4赞 Gareth Latty 5/19/2012
@HampusNilsson 列表推导与生成器表达式不同。As 和 短路,例如,如果 mine 上的第一个值计算结果为 ,将失败并且不再检查任何值,返回 .您的示例将执行相同的操作,只是它将首先生成整个比较列表,这意味着大量处理是徒劳的。all()any()Falseall()False
0赞 Charlie Parker 5/10/2021
如果你这样做,它也有效all([ cond(i) for i in range (n) ])
2赞 Gareth Latty 5/11/2021
@CharlieParker 正如我之前的评论中所讨论的那样,让它成为这样的列表理解只会让事情变慢。您希望使用生成器表达式(不带方括号),因为这样可能会短路。
0赞 Charlie Parker 5/11/2021
@GarethLatty啊,有趣的观点。不知道为什么优化会起作用,但我没有意识到它适用于列表作为输入,这是我关心的用例(例如调试或已经存在的列表)。谢谢你的提示!all
1赞 mulllhausen 11/8/2015 #4

这种方式比使用更灵活一些: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]

评论

0赞 tripleee 7/2/2019
你不能简单地说,甚至相应地为?我真的没有看到任何显着的改进。all_zeros = False in [x[2] == 0 for x in my_list]0 in [x[2] for x in my_list]any_zerosall()
0赞 mulllhausen 7/4/2019
不,您的版本 - 计算结果为 ,而我的版本计算结果为 。如果你把它改成,那么它就等价于我的。而且显然只会为.但我确实喜欢你想法的简洁性,所以我会更新我的答案all_zeros = False in [x[2] == 0 for x in my_list]FalseTrueall_zeros = not (False in [x[2] == 0 for x in my_list])0 in [x[2] for x in my_list]any_zeros
0赞 Learner 11/13/2015 #5

另一种使用方式。这检查了真实性和过程 (使用itertools.ifilterlambda)

样本-

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