【问题标题】:Check if a sublist contains an item检查子列表是否包含项目
【发布时间】:2012-12-05 16:39:05
【问题描述】:

我有一个 Python 列表。如下图所示,我想检查其中一个子列表是否包含项目。以下尝试失败。有谁知道一种简单的方法——不用我自己编写 for 循环?

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

我希望得到True,但返回的是False

【问题讨论】:

标签: python list


【解决方案1】:

我认为在这种情况下,我们可以通过将布尔表达式的求值委托给它自己的函数来从函数式编程中获得一些灵感。这样,如果您需要更改布尔条件的行为,您只需更改该函数定义!

假设您要检查子列表和 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 做了什么——因为你可以让你的变量名有意义,而不是临时列表理解变量的丛林。我并不特别关心函数式方法是否不那么 Pythonic - 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 的定义而不触及我们的主脚本来更改功能,这意味着更少的类型错误和更快的重构。

【讨论】:

    【解决方案2】:
    >>> a = [[1,2],[3,4],[5,6],7,8,9]
    >>> any(2 in i for i in a)
    True
    

    【讨论】:

    • any(8 in i for i in a) -> TypeError: argument of type 'int' is not iterable
    【解决方案3】:

    对于包含一些列表和一些整数的列表,在测试搜索目标是否在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 是否是一个列表,那么您将收到如下错误。 accepted answer 是错误的,因为它给出了这个错误。

    >>> 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
    

    【讨论】:

      【解决方案4】:

      我认为没有某种循环就无法进行测试。

      这是一个使用简单的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)
      

      【讨论】:

        【解决方案5】:

        简单的方法是:

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

        【讨论】:

        • TypeError: argument of type 'int' is not iterable
        猜你喜欢
        • 2012-08-26
        • 1970-01-01
        • 1970-01-01
        • 2018-01-18
        • 2023-03-26
        • 1970-01-01
        • 2020-04-02
        • 2014-03-02
        • 1970-01-01
        相关资源
        最近更新 更多