Tìm kiếm danh sách các đối tượng trong Python


94

Giả sử tôi đang tạo một lớp đơn giản để hoạt động tương tự như cấu trúc kiểu C, chỉ chứa các phần tử dữ liệu. Tôi đang cố gắng tìm cách tìm kiếm danh sách đối tượng cho các đối tượng có thuộc tính bằng một giá trị nhất định. Dưới đây là một ví dụ nhỏ để minh họa những gì tôi đang cố gắng làm.

Ví dụ:

class Data:
    pass

myList = []

for i in range(20):
    data = Data()
    data.n = i
    data.n_squared = i * i
    myList.append(data)

Tôi sẽ tìm kiếm danh sách myList như thế nào để xác định xem nó có chứa phần tử với n == 5 hay không?

Tôi đã lên Google và tìm kiếm tài liệu Python và tôi nghĩ rằng tôi có thể làm được điều này với khả năng hiểu danh sách, nhưng tôi không chắc. Nhân tiện, tôi có thể nói thêm rằng tôi đang phải sử dụng Python 2.4.3, vì vậy bất kỳ tính năng gee-whiz 2.6 hoặc 3.x nào mới không khả dụng với tôi.


Có lẽ là một ví dụ không chủ ý của bạn: myList = [Data (). N == 0, Data (). N = 1, ...] trong đó data.n sẽ được gán bởi range () và data.n sẽ là chỉ mục vào myList. Do đó, cho phép bạn kéo lên bất kỳ phiên bản Data () nào chỉ bằng cách tham chiếu myList theo một giá trị chỉ mục. Tất nhiên, sau đó bạn có thể sửa đổi myList [0] .n = 5.2 hoặc một cái gì đó. Và ví dụ có lẽ đã được đơn giản hóa quá mức.
DevPlayer

Câu trả lời:


139

Bạn có thể nhận được danh sách tất cả các phần tử phù hợp với khả năng hiểu danh sách:

[x for x in myList if x.n == 30]  # list of all elements with .n==30

Nếu bạn chỉ muốn xác định xem danh sách có chứa bất kỳ phần tử nào phù hợp và thực hiện nó (tương đối) hiệu quả hay không, bạn có thể

def contains(list, filter):
    for x in list:
        if filter(x):
            return True
    return False

if contains(myList, lambda x: x.n == 3)  # True if any element has .n==3
    # do stuff

25
hoặc, bất kỳ (custom_filter (x) cho x trong myList nếu xn == 30) chỉ là hàm "chứa" của bạn dưới dạng nội trang.
nosklo

Lỗi cú pháp trên nosklo - cần thêm một bộ () xung quanh trình tạo.
gahooa

Không phải vậy. Hãy thử nó và xem.
Robert Rossney

1
sẽ rất tốt nếu hợp nhất câu trả lời này với gahooa ( stackoverflow.com/a/598602/2349267 ).
Roman Hwang

77

Đơn giản, Thanh lịch và Mạnh mẽ:

Biểu thức trình tạo trong liên hợp với nội trang… (python 2.5+)

any(x for x in mylist if x.n == 10)

Sử dụng any()nội trang Python , được định nghĩa như sau:

any (có thể lặp lại) -> Trả về True nếu bất kỳ phần tử nào của có thể lặp lại là true. Tương đương với:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

Đẹp. FYI bạn có thể làm bất kỳ điều gì (x cho x trong danh sách của tôi nếu xn == 10) để tiết kiệm một số parens (cũng == không =).
Jacob Gabrielson

Tôi thích sử dụng any(x for x in mylist if x['n'] == 10)nhưng là ý tưởng tốt
Alex Montoya

47

Chỉ để hoàn thiện, chúng ta đừng quên Điều Đơn giản Nhất Có thể Làm được:

for i in list:
  if i.n == 5:
     # do something with it
     print "YAY! Found one!"

39
[x for x in myList if x.n == 30]               # list of all matches
[x.n_squared for x in myList if x.n == 30]     # property of matches
any(x.n == 30 for x in myList)                 # if there is any matches
[i for i,x in enumerate(myList) if x.n == 30]  # indices of all matches

def first(iterable, default=None):
  for item in iterable:
    return item
  return default

first(x for x in myList if x.n == 30)          # the first match, if any

1
Đây là một câu trả lời tốt vì phương pháp "đầu tiên", có lẽ là trường hợp sử dụng phổ biến nhất.
galarant

cảm ơn rất nhiều! chỉ số trận đấu là những gì tôi đang tìm kiếm. Có phím tắt nào để sử dụng điều này để lập chỉ mục trực tiếp danh sách để truy cập vào trường khác không? Bây giờ tôi nhận được một danh sách các mục danh sách (chỉ có một mục nhập, vì vậy đó là một danh sách có một mục). Để lấy chỉ mục, tôi cần thực hiện kết quả [0] trước khi có thể sử dụng nó để lập chỉ mục danh sách. Từ ví dụ câu hỏi, tôi muốn truy cập n_squared từ một n: myList cụ thể [chỉ mục của myList.n == 5] .n_squared
Frieke

32
filter(lambda x: x.n == 5, myList)

25
đối với những người muốn học Python, hiểu lambda là cơ bản.
vartec

2
Vâng, có và không - với khả năng hiểu danh sách và sắp xếp các chức năng chính như operator.attrgetter, tôi hầu như không bao giờ sử dụng lambdas.
Ben Hoyt

9

Bạn có thể sử dụng inđể tìm kiếm một mục trong bộ sưu tập và hiểu danh sách để trích xuất trường bạn quan tâm. Điều này (hoạt động cho danh sách, bộ, bộ giá trị và bất kỳ thứ gì xác định __contains__hoặc __getitem__).

if 5 in [data.n for data in myList]:
    print "Found it"

Xem thêm:


4

Bạn nên thêm một __eq__và một __hash__phương thức vào Datalớp của mình , nó có thể kiểm tra xem các __dict__thuộc tính có bằng nhau hay không (cùng thuộc tính) và sau đó nếu giá trị của chúng cũng bằng nhau.

Nếu bạn đã làm điều đó, bạn có thể sử dụng

test = Data()
test.n = 5

found = test in myList

Các inkiểm tra từ khóa nếu testlà trong myList.

Nếu bạn chỉ muốn aa nthuộc tính, Databạn có thể sử dụng:

class Data(object):
    __slots__ = ['n']
    def __init__(self, n):
        self.n = n
    def __eq__(self, other):
        if not isinstance(other, Data):
            return False
        if self.n != other.n:
            return False
        return True
    def __hash__(self):
        return self.n

    myList = [ Data(1), Data(2), Data(3) ]
    Data(2) in myList  #==> True
    Data(5) in myList  #==> False

3

Cân nhắc sử dụng từ điển:

myDict = {}

for i in range(20):
    myDict[i] = i * i

print(5 in myDict)

Hoặc: d = dict ((i, i * i) cho tôi trong phạm vi (20))
hughdbrown

Nó giải quyết vấn đề nhỏ mà tôi đã sử dụng để minh họa cho câu hỏi của mình, nhưng không thực sự giải quyết được câu hỏi gốc rễ của tôi. Câu trả lời tôi đang tìm kiếm (hơn 5 năm trước) là khả năng hiểu danh sách. :)
m0j0

1

Một cách khác mà bạn có thể làm là sử dụng hàm next ().

matched_obj = next(x for x in list if x.n == 10)
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.