【问题标题】:How to check if all elements of a list match a condition?如何检查列表的所有元素是否符合条件?
【发布时间】:2012-05-26 19:50:39
【问题描述】:

我有一个包含大约 20000 个列表的列表。我使用每个列表的第三个元素作为标志。我想对这个列表做一些操作,只要至少有一个元素的标志为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 循环中读取所有项目,并且它消耗大量时间!你有什么建议吗?

【问题讨论】:

  • 看起来您的数据结构不适合您的问题。如果您再解释一下上下文,也许我们可以提出更合适的建议。
  • 也许你可以用None[] 替换这些项目,因为你遍历列表而不是删除它们。在每次通过内部循环之前使用“check()”迭代所有项目来检查整个列表是一种非常缓慢的方法。

标签: python list for-loop while-loop


【解决方案1】:

这里最好的答案是使用all(),这是针对这种情况的内置函数。我们将其与generator expression 结合使用,以干净高效地产生您想要的结果。例如:

>>> 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 的 all 不接受像 Haskell 等这样的函数作为第一个参数。 al.,我也将答案更改为列表理解。 :)
  • @HampusNilsson 列表推导与生成器表达式不同。由于all()any() 短路,例如,如果我的第一个值评估为Falseall() 将失败并且不再检查任何值,返回False。您的示例将执行相同的操作,只是它会首先生成整个比较列表,这意味着大量处理都是徒劳的。
  • 如果你这样做all([ cond(i) for i in range (n) ])也可以工作
  • @CharlieParker 像我之前的评论中所讨论的那样,让它成为这样的列表理解只会让事情变慢。您想使用生成器表达式(没有方括号),因为那样它可能会短路。
  • @GarethLatty 啊,有趣的一点。不知道为什么优化有效,但我没有意识到 all 将列表作为输入工作,这是我关心的用例(例如调试或已经存在的列表)。不过感谢您的提示!
【解决方案2】:

这种方式比使用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() 有任何显着的改进。
  • 不,你的版本 - 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。但我确实喜欢你想法的简洁性,所以我会更新我的答案
【解决方案3】:

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

样本-

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 您可以删除all(...) 中的[...],因为它可以创建生成器而不是列表,这不仅可以节省两个字符,还可以节省内存和时间。通过使用生成器,一次只计算一个项目(以前的结果将被丢弃,因为不再使用),如果其中任何一个结果为False,生成器将停止计算其余的。
    • python3的注意事项; filter() 返回一个可迭代的,而不是一个列表。因此该行将是listb = list(filter(lamba x: x[2] != 0, listb))
    【解决方案5】:

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

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

    【讨论】:

      猜你喜欢
      • 2021-09-07
      • 1970-01-01
      • 2019-08-22
      • 1970-01-01
      • 2015-10-03
      • 2013-03-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多