【问题标题】:Find object in list that has attribute equal to some value (that meets any condition)在列表中查找属性等于某个值的对象(满足任何条件)
【发布时间】:2011-10-30 19:51:30
【问题描述】:

我有对象列表。我想在这个列表中找到一个(第一个或其他)对象,它的属性(或方法结果 - 不管)等于value

找到它的最佳方法是什么?

这里是测试用例:

  class Test:
      def __init__(self, value):
          self.value = value
          
  import random

  value = 5
  
  test_list = [Test(random.randint(0,100)) for x in range(1000)]
                    
  # that I would do in Pascal, I don't believe it's anywhere near 'Pythonic'
  for x in test_list:
      if x.value == value:
          print "i found it!"
          break
  

我认为使用生成器和reduce() 不会有任何区别,因为它仍然会遍历列表。

ps.:value 的等式只是一个例子。我们当然想得到满足任何条件的元素。

【问题讨论】:

标签: python django list


【解决方案1】:
next((x for x in test_list if x.value == value), None)

这会从列表中获取与条件匹配的第一个项目,如果没有项目匹配,则返回None。这是我首选的单表达式形式。

然而,

for x in test_list:
    if x.value == value:
        print("i found it!")
        break

简单的循环中断版本,完全符合 Python 风格——简洁、清晰、高效。使其与单线的行为相匹配:

for x in test_list:
    if x.value == value:
        print("i found it!")
        break
else:
    x = None

如果您不将break 排除在循环之外,这会将None 分配给x

【讨论】:

  • +1 表示“天真的循环中断版本,完全是 Pythonic”。
  • 很好的解决方案,但是我如何修改你的行,以便我可以使 x.value 实际上意味着 x.fieldMemberName 存储该名称的值? field = "name" next((x for x in test_list if x.field == value), None) 所以在这种情况下,我实际上是在检查 x.name,而不是 x.field
  • @StewartDale 您的要求并不完全清楚,但我认为您的意思是... if getattr(x, x.fieldMemberName) == value。这将从x 中获取具有存储在fieldMemberName 中的名称的属性,并将其与value 进行比较。
  • @ThatTechGuy -- else 子句用于for 循环,而不是if。 (拒绝编辑)。
  • @agf 哇,我真的不知道它的存在..book.pythontips.com/en/latest/for_-_else.htmlcool!
【解决方案2】:

因为它没有被提及只是为了完成。 过滤您要过滤的元素的好 ol' 过滤器。

函数式编程ftw。

####### Set Up #######
class X:

    def __init__(self, val):
        self.val = val

elem = 5

my_unfiltered_list = [X(1), X(2), X(3), X(4), X(5), X(5), X(6)]

####### Set Up #######

### Filter one liner ### filter(lambda x: condition(x), some_list)
my_filter_iter = filter(lambda x: x.val == elem, my_unfiltered_list)
### Returns a flippin' iterator at least in Python 3.5 and that's what I'm on

print(next(my_filter_iter).val)
print(next(my_filter_iter).val)
print(next(my_filter_iter).val)

### [1, 2, 3, 4, 5, 5, 6] Will Return: ###
# 5
# 5
# Traceback (most recent call last):
#   File "C:\Users\mousavin\workspace\Scripts\test.py", line 22, in <module>
#     print(next(my_filter_iter).value)
# StopIteration


# You can do that None stuff or whatever at this point, if you don't like exceptions.

我知道通常在 python 列表推导中是首选或至少 那是我读到的,但说实话,我没有看到这个问题。当然 Python 不是 FP 语言,但 Map / Reduce / Filter 是完全可读的,是函数式编程中最标准的标准用例。

所以你去。了解你的函数式编程。

过滤条件列表

没有比这更容易的了:

next(filter(lambda x: x.val == value,  my_unfiltered_list)) # Optionally: next(..., None) or some other default value to prevent Exceptions

【讨论】:

  • 我很喜欢这种风格,但有两个潜在的问题。 1:仅适用于 Python 3;在 Python 2 中,filter 返回一个与next 不兼容的列表。 2:它要求有一个明确的匹配,否则你会得到一个StopIteration异常。
  • 1:我不知道 Python 2。当我开始使用 Python 时,Python 3 已经可用。不幸的是,我对 Python 2 的规范一无所知。 2. @freethebees 正如 agf 所指出的那样。如果您不喜欢异常,您可以使用 next(..., None) 或其他一些默认值。我还将它作为注释添加到我的代码中。
  • @freethebees 第 2 点实际上可能很好。当我需要列表中的某个对象时,快速失败是一件好事。
  • @freethebees 你不应该在 2020 年 1 月 1 日之后使用 python 2,因为它已经结束了对它的支持。
  • @vrnvorona 我的评论是在 2018 年发表的
【解决方案3】:

一个简单的例子: 我们有以下数组

li = [{"id":1,"name":"ronaldo"},{"id":2,"name":"messi"}]

现在,我们要在数组中找到 id 等于 1 的对象

  1. 使用 next 方法和列表理解
next(x for x in li if x["id"] == 1 )
  1. 使用列表理解并返回第一项
[x for x in li if x["id"] == 1 ][0]
  1. 自定义函数
def find(arr , id):
    for x in arr:
        if x["id"] == id:
            return x
find(li , 1)

以上所有方法的输出为{'id': 1, 'name': 'ronaldo'}

【讨论】:

  • 如果 ID 是字符串而不是 int 怎么办?
  • @Lisa-Marie 您可以完全按照上面的示例进行操作。 li = [{"id":"id1", "name":"ronaldo"},{"id":"id2", "name":"messi"}]你可以在上述解决方案中使用这个条件x["id"] == "id1"
【解决方案4】:

老问题,但我经常使用这个问题(对于 3.8 版)。它有点语法盐,但它比最佳答案具有优势,因为您可以通过简单地删除 [0] 来检索结果列表(如果有多个),如果没有找到它仍然默认为 None .对于任何其他情况,只需将 x.value==value 更改为您要查找的任何内容。

_[0] if (_:=[x for x in test_list if x.value==value]) else None

【讨论】:

    【解决方案5】:

    您还可以通过__eq__ 方法为您的Test 类实现丰富的比较,并使用in 运算符。 不确定这是否是最好的独立方法,但如果您需要基于 value 在其他地方比较 Test 实例,这可能会很有用。

    class Test:
        def __init__(self, value):
            self.value = value
    
        def __eq__(self, other):
            """To implement 'in' operator"""
            # Comparing with int (assuming "value" is int)
            if isinstance(other, int):
                return self.value == other
            # Comparing with another Test object
            elif isinstance(other, Test):
                return self.value == other.value
    
    import random
    
    value = 5
    
    test_list = [Test(random.randint(0,100)) for x in range(1000)]
    
    if value in test_list:
        print "i found it"
    

    【讨论】:

      【解决方案6】:

      我刚刚遇到了一个类似的问题,并针对列表中没有对象满足要求的情况设计了一个小优化。(对于我的用例,这导致了重大的性能提升):

      与列表 test_list 一起,我保留了一个附加集 test_value_set,其中包含我需要过滤的列表值。所以这里 agf 解决方案的 else 部分变得非常快。

      【讨论】:

        【解决方案7】:

        你可以这样做

        dict = [{
           "id": 1,
           "name": "Doom Hammer"
         },
         {
            "id": 2,
            "name": "Rings ov Saturn"
         }
        ]
        
        for x in dict:
          if x["id"] == 2:
            print(x["name"])
        

        这就是我用来在一长串对象中查找对象的方法。

        【讨论】:

        • 这与提问者已经尝试过的有什么不同?
        • 我想展示他如何以最简单的方式获取对象和对象数组。
        【解决方案8】:

        对于下面的代码,xGen 是一个匿名生成器表达式,yFilt 是一个过滤器对象。请注意,对于 xGen,会返回额外的 None 参数,而不是在列表耗尽时抛出 StopIteration。

        arr =((10,0), (11,1), (12,2), (13,2), (14,3))
        
        value = 2
        xGen = (x for x in arr if x[1] == value)
        yFilt = filter(lambda x: x[1] == value, arr)
        print(type(xGen))
        print(type(yFilt))
        
        for i in range(1,4):
            print('xGen: pass=',i,' result=',next(xGen,None))
            print('yFilt: pass=',i,' result=',next(yFilt))
        

        输出:

        <class 'generator'>
        <class 'filter'>
        xGen: pass= 1  result= (12, 2)
        yFilt: pass= 1  result= (12, 2)
        xGen: pass= 2  result= (13, 2)
        yFilt: pass= 2  result= (13, 2)
        xGen: pass= 3  result= None
        Traceback (most recent call last):
          File "test.py", line 12, in <module>
            print('yFilt: pass=',i,' result=',next(yFilt))
        StopIteration
        

        【讨论】:

          【解决方案9】:

          如果您正在 Python 中查找数组中的对象。您可以使用 if 条件。

           model_t1 = [0,1,2,3,4]
           model_t2 = [7,8,9,15,14]
           _data = model_t1
          
           for md in model_t2:
               _data.append(md)
          
           for p_data in _data:
               if len(p_data['Property']) == 'Value':
                  print(json(p_data))
          

          【讨论】:

            猜你喜欢
            • 2016-05-01
            • 1970-01-01
            • 2020-09-08
            • 1970-01-01
            • 2022-06-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-10-07
            相关资源
            最近更新 更多