【问题标题】:Using Python's list index() method on a list of tuples or objects?在元组或对象列表上使用 Python 的 list index() 方法?
【发布时间】:2010-10-31 02:49:54
【问题描述】:

Python 的列表类型有一个 index() 方法,它接受一个参数并返回列表中与该参数匹配的第一项的索引。例如:

>>> some_list = ["apple", "pear", "banana", "grape"]
>>> some_list.index("pear")
1
>>> some_list.index("grape")
3

有没有一种优雅的(惯用的)方法可以将它扩展到复杂对象的列表,比如元组?理想情况下,我希望能够做这样的事情:

>>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
>>> some_list.getIndexOfTuple(1, 7)
1
>>> some_list.getIndexOfTuple(0, "kumquat")
2

getIndexOfTuple() 只是一个假设的方法,它接受一个子索引和一个值,然后返回具有该子索引处给定值的列表项的索引。希望

是否有某种方法可以实现该一般结果,使用列表推导或lambas 或类似“内联”的东西?我想我可以编写自己的类和方法,但如果 Python 已经有办法做到这一点,我不想重新发明轮子。

【问题讨论】:

    标签: python list tuples reverse-lookup


    【解决方案1】:

    这个怎么样?

    >>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
    >>> [x for x, y in enumerate(tuple_list) if y[1] == 7]
    [1]
    >>> [x for x, y in enumerate(tuple_list) if y[0] == 'kumquat']
    [2]
    

    正如 cmets 中所指出的,这将获得所有匹配项。要获得第一个,您可以这样做:

    >>> [y[0] for y in tuple_list].index('kumquat')
    2
    

    在 cmets 中有一个很好的讨论,关于发布的所有解决方案之间的速度差异。我可能有点偏颇,但我个人会坚持单线,因为我们谈论的速度与为这个问题创建函数和导入模块相比微不足道,但如果你打算这样做很多您可能希望查看提供的其他答案的元素,因为它们比我提供的要快。

    【讨论】:

    • 不错的解决方案,但不会产生所需的结果:它不会只返回第一项的索引,而是会遍历整个列表并返回所有匹配项。
    • 仍然在内存中创建一个大小为 N 的新列表,这不是必需的。也在 O(n) 平均情况下运行,可以改进为 O(n/2)。是的,我知道从技术上讲这仍然是 O(n)。
    • 只需从多个匹配项列表中选择第一个结果 ([0]),即可轻松解决 van raise 的问题。有趣的是,如果我对我的答案进行与 cmets 中相同的速度测试 a) Paolo 的原始枚举理解,b) Paolo 的修订理解和索引,以及 c) 我的答案中的 map/operator/index 方法,选项 C是 tuple_list 中有多个匹配项的时间(即:多个“kumquat”)。 B 次之。 A 最慢。这很有趣!
    • 你能把三联画扔进那个测试吗? :)
    • 虽然我应该重复一遍,这个测试显然是一次性的,在可能无法反映某人的生产需求的条件下完成,并且几乎没有精心计划,所以带着卡车装载它盐。
    【解决方案2】:

    一段时间后,这些列表推导变得混乱。

    我喜欢这种 Pythonic 方法:

    from operator import itemgetter
    
    def collect(l, index):
       return map(itemgetter(index), l)
    
    # And now you can write this:
    collect(tuple_list,0).index("cherry")   # = 1
    collect(tuple_list,1).index("3")        # = 2
    

    如果您需要您的代码具有超强性能:

    # Stops iterating through the list as soon as it finds the value
    def getIndexOfTuple(l, index, value):
        for pos,t in enumerate(l):
            if t[index] == value:
                return pos
    
        # Matches behavior of list.index
        raise ValueError("list.index(x): x not in list")
    
    getIndexOfTuple(tuple_list, 0, "cherry")   # = 1
    

    【讨论】:

    • +1 作为超级性能确实是发布的最快的解决方案。我个人仍然会坚持使用一个班轮,因为这个级别的速度差异非常没有意义,但无论如何还是很高兴知道。
    • 谢谢。通常我会使用 collect() 版本 - 看起来好多了。
    【解决方案3】:

    一种可能性是使用operator 模块中的itemgetter 函数:

    import operator
    
    f = operator.itemgetter(0)
    print map(f, tuple_list).index("cherry") # yields 1
    

    itemgetter 的调用返回一个函数,该函数将对传递给它的任何内容执行与foo[0] 等效的操作。使用map,然后将该函数应用于每个元组,将信息提取到一个新列表中,然后像往常一样调用index

    map(f, tuple_list)
    

    相当于:

    [f(tuple_list[0]), f(tuple_list[1]), ...etc]
    

    这又相当于:

    [tuple_list[0][0], tuple_list[1][0], tuple_list[2][0]]
    

    给出:

    ["pineapple", "cherry", ...etc]
    

    【讨论】:

    • 这很好。我想知道这个或列表理解是否更快?无论哪种方式,+1。
    • 这个问题是你要迭代两次来获取索引。
    • Paolo 提出了一个有趣的问题...正如我认为每个人都怀疑的那样,列表理解和枚举方法稍快...在我的科学测试中运行超过 100000 次,枚举方法是大约快 10 毫秒。
    • 酷。感谢您进行测试。
    • 我认为 Paolo 和我应该混合答案 :-) 在他编辑了他的答案之后,我重新运行了速度测试,以解决 tuple_list 中存在多个匹配项的情况......以及操作员方法是最快的......请参阅我在 Paolo 的回答中的评论。
    【解决方案4】:

    您可以使用列表理解和 index() 来做到这一点

    tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
    [x[0] for x in tuple_list].index("kumquat")
    2
    [x[1] for x in tuple_list].index(7)
    1
    

    【讨论】:

      【解决方案5】:

      this question 的启发,我觉得这很优雅:

      >>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
      >>> next(i for i, t in enumerate(tuple_list) if t[1] == 7)
      1
      >>> next(i for i, t in enumerate(tuple_list) if t[0] == "kumquat")
      2
      

      【讨论】:

        【解决方案6】:

        我会将此作为对 Triptych 的评论,但由于缺乏评级,我还不能发表评论:

        使用枚举器方法匹配元组列表中的子索引。 例如

        li = [(1,2,3,4), (11,22,33,44), (111,222,333,444), ('a','b','c','d'),
                ('aa','bb','cc','dd'), ('aaa','bbb','ccc','ddd')]
        
        # want pos of item having [22,44] in positions 1 and 3:
        
        def getIndexOfTupleWithIndices(li, indices, vals):
        
            # if index is a tuple of subindices to match against:
            for pos,k in enumerate(li):
                match = True
                for i in indices:
                    if k[i] != vals[i]:
                        match = False
                        break;
                if (match):
                    return pos
        
            # Matches behavior of list.index
            raise ValueError("list.index(x): x not in list")
        
        idx = [1,3]
        vals = [22,44]
        print getIndexOfTupleWithIndices(li,idx,vals)    # = 1
        idx = [0,1]
        vals = ['a','b']
        print getIndexOfTupleWithIndices(li,idx,vals)    # = 3
        idx = [2,1]
        vals = ['cc','bb']
        print getIndexOfTupleWithIndices(li,idx,vals)    # = 4
        

        【讨论】:

          【解决方案7】:

          好的,可能是vals(j)的错误,更正的是:

          def getIndex(li,indices,vals):
          for pos,k in enumerate(lista):
              match = True
              for i in indices:
                  if k[i] != vals[indices.index(i)]:
                      match = False
                      break
              if(match):
                  return pos
          

          【讨论】:

            【解决方案8】:
            z = list(zip(*tuple_list))
            z[1][z[0].index('persimon')]
            

            【讨论】:

              【解决方案9】:
              tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
              
              def eachtuple(tupple, pos1, val):
                  for e in tupple:
                      if e == val:
                          return True
              
              for e in tuple_list:
                  if eachtuple(e, 1, 7) is True:
                      print tuple_list.index(e)
              
              for e in tuple_list:
                  if eachtuple(e, 0, "kumquat") is True:
                      print tuple_list.index(e)
              

              【讨论】:

                【解决方案10】:

                Python 的 list.index(x) 返回列表中第一次出现 x 的索引。所以我们可以通过列表压缩返回的对象来获取它们的索引。

                >>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
                >>> [tuple_list.index(t) for t in tuple_list if t[1] == 7]
                [1]
                >>> [tuple_list.index(t) for t in tuple_list if t[0] == 'kumquat']
                [2]
                

                同样的行,如果有多个匹配的元素,我们也可以得到索引列表。

                >>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11), ("banana", 7)]
                >>> [tuple_list.index(t) for t in tuple_list if t[1] == 7]
                [1, 4]
                

                【讨论】:

                • 您好,欢迎来到 StackOverflow。请为您的答案添加一些解释。
                • 这是accidentally quadratic。相反,您应该使用enumerate[idx for idx, t in enumerate(tuple_list) if t[1] == 7]
                【解决方案11】:

                我想以下方法不是最好的方法(速度和优雅问题),但它可能会有所帮助:

                from collections import OrderedDict as od
                t = [('pineapple', 5), ('cherry', 7), ('kumquat', 3), ('plum', 11)]
                list(od(t).keys()).index('kumquat')
                2
                list(od(t).values()).index(7)
                7
                # bonus :
                od(t)['kumquat']
                3
                

                2个成员的元组列表可以直接转换为有序dict,数据结构其实是一样的,所以我们可以即时使用dict方法。

                【讨论】:

                  【解决方案12】:

                  这也可以使用 Lambda 表达式:

                  l = [('rana', 1, 1), ('pato', 1, 1), ('perro', 1, 1)]
                  map(lambda x:x[0], l).index("pato") # returns 1 
                  
                  编辑以添加示例:
                  l=[['rana', 1, 1], ['pato', 2, 1], ['perro', 1, 1], ['pato', 2, 2], ['pato', 2, 2]]
                  

                  按条件提取所有项目:

                  filter(lambda x:x[0]=="pato", l) #[['pato', 2, 1], ['pato', 2, 2], ['pato', 2, 2]]
                  

                  使用索引按条件提取所有项目:

                  >>> filter(lambda x:x[1][0]=="pato", enumerate(l))
                  [(1, ['pato', 2, 1]), (3, ['pato', 2, 2]), (4, ['pato', 2, 2])]
                  >>> map(lambda x:x[1],_)
                  [['pato', 2, 1], ['pato', 2, 2], ['pato', 2, 2]]
                  

                  注意:_ 变量仅适用于交互式解释器。更一般地,必须明确分配_,即_=filter(lambda x:x[1][0]=="pato", enumerate(l))

                  【讨论】:

                  • 我认为这个解决方案 (map(lambda x:x[0], l).index("pato")) 实际上是更好的解决方案之一,但我怀疑作者不会说英语。有人愿意重写这个吗?或者,如果作者不会说英语,这个社区是否可以从头开始完全重写答案?
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-07-07
                  • 2017-01-21
                  • 2023-03-03
                  • 2019-01-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多