检查子列表是否包含某个项

9

我有一个Python的列表,其中包含多个子列表。如下所示,我想检查其中是否有一个子列表包含某个元素。以下尝试失败了。有没有简单的方法可以做到这一点 -- 而不是我自己写循环?

>>> a = [[1,2],[3,4],[5,6],7,8,9]
>>> 2 in a

我原本期望的是True,但实际返回的是False

1
这是如何“展平列表”的问题的变体(https://dev59.com/THI95IYBdhLWcg3wyBCc)。 - Blckknght
这个回答解决了你的问题吗?检查一个项是否在嵌套列表中 - Tomerikoo
5个回答

19
>>> a = [[1,2],[3,4],[5,6],7,8,9]
>>> any(2 in i for i in a)
True

2
any(8 in i for i in a) -> TypeError: argument of type 'int' is not iterable - kaya3

4

对于包含一些列表和一些整数的列表,您需要在测试搜索目标是否在i中之前先测试元素i是否为列表。

>>> any(2 in i for i in a if isinstance(i, list))
True
>>> any(8 in i for i in a if isinstance(i, list))
False

如果您没有检查i是否为列表,那么您将会得到下面的错误。这篇被接受的答案是错误的,因为它会出现这个错误。
>>> any(8 in i for i in a)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    any(8 in i for i in a)
  File "<pyshell#3>", line 1, in <genexpr>
    any(8 in i for i in a)
TypeError: argument of type 'int' is not iterable

2

我认为这种情况可以从函数式编程中汲取灵感,通过将布尔表达式的评估委托给自己的函数。这样,如果您需要更改布尔条件的行为,只需更改该函数定义!

假设您想检查子列表并且还要检查顶级中出现的int。我们可以定义一个函数,在单个列表元素上执行比较时返回布尔值:

def elem(a, b):
    '''
    Defines if an object b matches a.
    '''

    return (isinstance(b, int) and a == b) or (isinstance(b, list) and a in b)

需要注意的是,这个函数并没有涉及到我们的列表 - 在我们的使用中,参数b只是列表中的一个单独元素,但我们同样可以用它来比较两个值。现在我们有了以下结果:

>>> a = [[1,2],[3,4],[5,6],7,8,9]

>>> any(elem(2, i) for i in a)
True

>>> any(elem(8, i) for i in a)
True

>>> any(elem(10, i) for i in a)
False

太棒了!这种定义的另一个好处是它允许您部分应用函数,并使您能够为仅搜索一种类型的数字指定名称:

from functools import partial

>>> contains2 = partial(elem, 2)

>>> any(map(contains2, a))
True

>>> b = [[1],[3,4],[5,6],7,8,9]]
>>> any(map(contains2, b))
False

在我看来,这样做可以让代码更易读,虽然需要一些样板代码和了解map的用法 - 因为你可以使用有意义的变量名,而不是一堆临时列表推导式变量。我并不特别在意函数式方法是否符合Python风格 - Python是一种多范式语言,我认为这样看起来更好,简单明了。但这是个人选择 - 这取决于你自己。

现在假设我们的情况已经改变,我们现在只想检查子列表 - 在顶层出现不够。这没问题,因为现在我们需要改变的只是elem的定义。让我们看一下:

def elem(a, b):
    return isinstance(b, list) and a in b

我们刚刚删除了当 b 是顶级整数时匹配的可能性!现在如果运行这段代码:

>>> a = [[1,2],[3,4],[5,6],7,8,9,"a",["b","c"]]
>>> any(elem(2, i) for i in a)
True
>>> any(elem(8, i) for i in a)
False

我将举例说明一个最后的例子,它真正展示了这种定义方式的强大之处。假设我们有一个任意深度嵌套的整数列表。如何检查整数是否在任何一级中?
我们可以采用递归方法 - 而且几乎不需要进行修改:
def elem(a, b):
    return (isinstance(b, int) and a == b) or \
           (isinstance(b, list) and any(map(partial(elem, a), b)))

因为我们使用了这个递归定义,它被定义为在单个元素上执行,所以之前的所有代码行仍然有效:
>>> d = [1, [2, [3, [4, 5]]]]
>>> any(elem(1, i) for i in d)
True
>>> any(elem(4, i) for i in d)
True
>>> any(elem(10, i) for i in d)
False
>>> any(map(contains2, d))
True

当然,鉴于此函数现在是递归的,我们实际上可以直接调用它:
>>> elem(4, d)
True

但问题在于,这种模块化的方法使我们只需更改elem的定义而不必触及主要脚本,从而可以改变功能,这意味着更少的类型错误和更快的重构。


-1

我认为没有任何一种方法可以在不使用某种循环的情况下进行测试。

这里有一个函数,它使用直接的for循环来明确地检查子列表中是否存在对象:

def sublist_contains(lst, obj):
    for item in lst:
        try:
            if obj in item:
                return True
        except TypeError:
            pass
    return False

当然,这并不测试对象是否在顶层列表中,如果有多个嵌套级别,则也无法工作。下面是一个更通用的解决方案,使用递归将循环放入生成器表达式中,并将其传递给内置函数any
def nested_contains(lst, obj):
    return any(item == obj or
               isinstance(item, list) and nested_contains(item, obj)
               for item in lst)

-1

简单的方法是:

  a = [[1,2],[3,4],[5,6],7,8,9]
  result = [2 in i for i in a]

  True in result --> True

1
TypeError: argument of type 'int' is not iterable - kaya3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接