Tìm đối tượng trong danh sách có thuộc tính bằng một số giá trị (đáp ứng bất kỳ điều kiện nào)


221

Tôi đã có danh sách các đối tượng. Tôi muốn tìm một đối tượng (đầu tiên hoặc bất cứ điều gì) trong danh sách này có thuộc tính (hoặc kết quả phương thức - bất cứ điều gì) bằng value.

Cách tốt nhất để tìm thấy nó là gì?

Đây là trường hợp thử nghiệm:

  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

Tôi nghĩ rằng việc sử dụng máy phát điện và reduce()sẽ không tạo ra bất kỳ sự khác biệt nào vì nó vẫn sẽ được lặp qua danh sách.

ps.: phương trình valuechỉ là một ví dụ. Tất nhiên chúng tôi muốn có được yếu tố đáp ứng bất kỳ điều kiện.


2
Đây là một cuộc thảo luận tốt về câu hỏi này: tomayko.com/writings/cleanest-python-find-in-list-feft
Andrew Hare

Các bài gốc là lố bịch trong ngày, nhưng câu trả lời thứ 2 phù hợp với phiên bản một dòng của tôi chính xác. Tôi không tin nó tốt hơn phiên bản vòng lặp cơ bản.
agf

Câu trả lời:


432
next((x for x in test_list if x.value == value), None)

Điều này nhận được mục đầu tiên từ danh sách phù hợp với điều kiện và trả về Nonenếu không có mục nào khớp. Đó là hình thức biểu thức đơn ưa thích của tôi.

Tuy nhiên,

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

Phiên bản break-break ngây thơ, hoàn toàn là Pythonic - nó ngắn gọn, rõ ràng và hiệu quả. Để làm cho nó phù hợp với hành vi của một-liner:

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

Điều này sẽ gán Noneđể xnếu bạn không breakra khỏi vòng lặp.


72
+1 cho sự yên tâm "Phiên bản phá vỡ vòng lặp ngây thơ, hoàn toàn là Pythonic".
LaundroMat

giải pháp tuyệt vời, nhưng làm cách nào để sửa đổi dòng của bạn để tôi có thể tạo x.value thực sự có nghĩa là x.fieldMemberName nơi tên đó được lưu trữ trong giá trị? trường = "tên" tiếp theo ((x cho x trong test_list nếu x.field == value), Không có) để trong trường hợp này, tôi thực sự đang kiểm tra đối với x.name, không phải x.field
Stewart Dale

3
@StewartDale Không hoàn toàn rõ ràng những gì bạn đang hỏi, nhưng tôi nghĩ bạn có ý đó ... if getattr(x, x.fieldMemberName) == value. Điều đó sẽ tìm nạp thuộc tính từ xvới tên được lưu trữ fieldMemberNamevà so sánh nó với value.
agf

1
@ThatTechGuy - elseMệnh đề có nghĩa là trên forvòng lặp, không phải là if. (Từ chối chỉnh sửa).
agf

1
@agf Wow Tôi thực sự không có ý tưởng nào tồn tại .. book.pythontips.com/en/latest/for_-_else.html tuyệt vời!
ThatTechGuy

25

Vì nó đã không được đề cập chỉ để hoàn thành. Bộ lọc ol 'tốt để lọc các phần tử được lọc của bạn.

Chức năng lập trình 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.

Tôi biết rằng nói chung trong việc hiểu danh sách python được ưa thích hoặc ít nhất đó là những gì tôi đọc, nhưng tôi không thấy vấn đề này là trung thực. Tất nhiên Python không phải là ngôn ngữ FP, nhưng Map / Giảm / Bộ lọc hoàn toàn có thể đọc được và là tiêu chuẩn nhất của các trường hợp sử dụng tiêu chuẩn trong lập trình chức năng.

Vì vậy, có bạn đi. Biết lập trình chức năng của bạn.

danh sách điều kiện lọc

Nó sẽ không dễ dàng hơn thế này:

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

Tôi khá thích phong cách này nhưng có hai vấn đề tiềm ẩn. 1 : Nó chỉ hoạt động trong Python 3; trong Python 2, filtertrả về một danh sách không tương thích next. 2 : nó yêu cầu phải có một trận đấu xác định, nếu không bạn sẽ có một StopIterationngoại lệ.
freethebees

1
1: Tôi không biết về Python 2. Khi tôi bắt đầu sử dụng Python, Python 3 đã có sẵn. Thật không may, tôi không biết gì về các đặc tả của Python 2. 2. @freethebees như được chỉ ra bởi agf. Bạn có thể sử dụng tiếp theo (..., Không có) hoặc một số giá trị mặc định khác, nếu bạn không phải là người thích ngoại lệ. Tôi cũng đã thêm nó như là một nhận xét cho mã của tôi.
Nima Mousavi

@freethebees Điểm 2 có thể thực sự tốt. Khi tôi yêu cầu một đối tượng nhất định trong danh sách, thất bại nhanh là một điều tốt.
kap

7

Một ví dụ đơn giản : Chúng ta có mảng sau

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

Bây giờ, chúng tôi muốn tìm đối tượng trong mảng có id bằng 1

  1. Sử dụng phương pháp nextvới danh sách hiểu
next(x for x in li if x["id"] == 1 )
  1. Sử dụng danh sách hiểu và trả về mục đầu tiên
[x for x in li if x["id"] == 1 ][0]
  1. Chức năng tùy chỉnh
def find(arr , id):
    for x in arr:
        if x["id"] == id:
            return x
find(li , 1)

Đầu ra tất cả các phương pháp trên là {'id': 1, 'name': 'ronaldo'}


1

Tôi vừa gặp phải một vấn đề tương tự và đã nghĩ ra một tối ưu hóa nhỏ cho trường hợp không có đối tượng nào trong danh sách đáp ứng yêu cầu (đối với trường hợp sử dụng của tôi, điều này dẫn đến cải thiện hiệu suất lớn):

Cùng với danh sách test_list, tôi giữ một bộ test_value_set bổ sung bao gồm các giá trị của danh sách mà tôi cần lọc. Vì vậy, đây là một phần khác của giải pháp của agf trở nên rất nhanh.


1

Bạn có thể làm một cái gì đó như thế này

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

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

Đó là những gì tôi sử dụng để tìm các đối tượng trong một mảng dài các đối tượng.


Làm thế nào là khác nhau sau đó những gì người hỏi đã thử?
Anum Sheraz

Tôi muốn chỉ ra làm thế nào anh ta có thể lấy đối tượng và mảng các đối tượng một cách đơn giản nhất.
Illud

0

Bạn cũng có thể thực hiện so sánh phong phú thông qua __eq__phương thức cho Testlớp của bạn và sử dụng intoán tử. Không chắc chắn nếu đây là cách độc lập tốt nhất, nhưng trong trường hợp nếu bạn cần so sánh các Testtrường hợp dựa trên valuemột nơi khác, điều này có thể hữu ích.

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

Đối với mã bên dưới, xGen là một biểu thức trình tạo anonomous, yFilt là một đối tượng bộ lọc. Lưu ý rằng đối với xGen, tham số Không bổ sung được trả về thay vì ném StopIteration khi danh sách đã hết.

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

Đầu ra:

<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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.