在列表中查找属性等于某个值(满足任何条件)的对象


219

我有对象列表。我想在此列表中找到一个属性(或方法结果-任意)等于的(第一个或任何对象)对象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 isn't anywhere near 'Pythonic'
  for x in test_list:
      if x.value == value:
          print "i found it!"
          break

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

ps .:方程式to value只是一个例子。当然我们要获得满足任何条件的元素。


2
这里是对这个问题的很好的讨论:tomayko.com/writings/cleanest-python-find-in-list-function
Andrew Hare

原始帖子太荒谬了,但是第二个回复恰好与我的单行版本匹配。我不相信它比基本循环版本更好。
2011年

Answers:


432
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

天真的循环中断版本完全是Pythonic的-简洁,清晰和高效。为了使其与单线行为相匹配:

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

如果您不在循环之外,这将分配None给。xbreak


72
+1代表令人放心的“天真的循环破解版本,完全是Pythonic”。
LaundroMat 2011年

很好的解决方案,但是如何修改您的行,以便可以使x.value实际表示该名称存储在value中的x.fieldMemberName?field =“ name” next((如果x.field == value,x表示test_list中的x,x,无),因此在这种情况下,我实际上是在对x.name进行检查,而不是x.field
Stewart Dale

3
@StewartDale尚不清楚您要问什么,但我认为您的意思是... if getattr(x, x.fieldMemberName) == value。它将x使用存储在其中的名称获取属性fieldMemberName,并将其与进行比较value
agf

1
@ThatTechGuy- else子句应该在for循环中,而不是在if。(拒绝编辑)。
agf

1
@agf哇,我真的不知道存在.. book.pythontips.com/en/latest/for_-_else.html酷!
ThatTechGuy

24

由于尚未提及仅仅是为了完成。好的ol'过滤器可过滤您要过滤的元素。

函数编程

####### 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返回与不兼容的列表next2:它要求有一个确定的匹配项,否则您将得到一个StopIteration例外。
freethebees

1
1:我不了解Python2。当我开始使用Python时,Python 3已经可用。不幸的是,我对Python 2的规范一无所知。agf指出了@freethebees。如果您不喜欢例外,则可以使用next(...,None)或其他一些默认值。我也将其添加为代码注释。
尼玛·穆萨维

@freethebees点2可能实际上很好。当我需要列表中的某个对象时,快速失败是一件好事。
KAP

7

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

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


1

我遇到了一个类似的问题,针对列表中没有对象符合要求的情况设计了一个小型优化(对于我的用例,这导致了重大的性能改进):

与列表test_list一起,我还有一个额外的集合test_value_set,它由我需要过滤的列表的值组成。因此,这里的agf解决方案的其他部分变得非常快。


1

你可以做这样的事情

dict = [{
   "id": 1,
   "name": "Doom Hammer"
 },
 {
    "id": 2,
    "name": "Rings ov Saturn"
 }
]

for x in dict:
  if x["id"] == 2:
    print(x["name"])

那就是我用来在长对象数组中查找对象的东西。


那么,什么提问者已经尝试过了呢?
阿努姆·谢拉兹

我想展示他如何以最简单的方式获取对象和对象数组。
Illud

0

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

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"

0

对于下面的代码,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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.