Пошук у списку об’єктів у Python


94

Припустимо, я створюю простий клас для роботи, подібної до структури стилю C, щоб просто утримувати елементи даних. Я намагаюся зрозуміти, як шукати в списку об'єктів об'єкти з атрибутом, що дорівнює певному значенню. Нижче наведено тривіальний приклад для ілюстрації того, що я намагаюся зробити.

Наприклад:

class Data:
    pass

myList = []

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

Як би я розпочав пошук у списку myList, щоб визначити, чи містить він елемент з n == 5?

Я гуглював і шукав документи в Python, і думаю, що міг би це зробити із розумінням списку, але я не впевнений. Я можу додати, що мені до речі доводиться використовувати Python 2.4.3, тому будь-які нові функції gee-whiz 2.6 або 3.x мені недоступні.


Можливо, ненавмисна химерність вашого прикладу: myList = [Data (). N == 0, Data (). N = 1, ...], де data.n буде призначено за діапазоном (), а data.n буде індекс в myList. Тому дозволяється витягувати будь-який екземпляр Data (), просто посилаючись на myList значенням індексу. Звичайно, ви можете пізніше змінити myList [0] .n = 5.2 або щось інше. І приклад, можливо, був занадто спрощений.
DevPlayer

Відповіді:


139

Ви можете отримати список усіх відповідних елементів із розумінням списку:

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

Якщо ви просто хочете визначити, чи містить список будь-який елемент, який відповідає, і зробити це (відносно) ефективно, ви можете це зробити

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
або будь-який (custom_filter (x) для x у myList, якщо xn == 30), який є лише вашою функцією "містить" як вбудований.
nosklo

Синтаксична помилка на nosklo - потрібен додатковий набір () навколо генератора.
gahooa

Не так. Спробуйте і подивіться.
Роберт Росней,

1
було б добре , щоб об'єднати цю відповідь разом з gahooa в ( stackoverflow.com/a/598602/2349267 ).
Роман Хванг

77

Простий, елегантний та потужний:

Вираз генератора спільно з вбудованим… (python 2.5+)

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

Використовує any()вбудований Python , який визначається наступним чином:

any (iterable) -> Return True, якщо будь-який елемент iterable є true. Дорівнює:

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

Приємно. FYI, ви можете зробити будь-яке (x для x у моєму списку, якщо xn == 10), щоб зберегти деякі паренси (також == not =).
Jacob Gabrielson

Я віддаю перевагу використанню, any(x for x in mylist if x['n'] == 10)але це гарна ідея
Alex Montoya

47

Тільки для повноти, не забуваємо про найпростішу справу, яка могла б працювати:

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
Це хороша відповідь через "перший" метод, який, мабуть, є найпоширенішим варіантом використання.
галарант

щиро дякую! індекси відповідності - це те, що я шукав. Чи існує ярлик, за допомогою якого це можна безпосередньо індексувати список для доступу до іншого поля? Тепер я отримую список записів у списку (є лише один запис, тож це список з одним елементом). Щоб отримати індекс, мені потрібно виконати результат [0], перш ніж я зможу використовувати його для індексації списку. З прикладу запитання я хочу отримати доступ до n_squared з певного n: myList [індекс myList.n == 5] .n_squared
Фріке

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

25
для тих, хто хоче вивчити Python, розуміння лямбда є основним.
vartec 01.03.09

2
Ну, так і ні - з розумінням списків та сортуванням ключових функцій, таких як operator.attrgetter, я майже ніколи не використовую lambdas.
Бен Хойт

9

Ви можете використовувати inдля пошуку елемента в колекції та розуміння списку для вилучення поля, яке вас цікавить. Це (працює для списків, наборів, кортежів та всього, що визначає __contains__або __getitem__).

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

Дивіться також:


4

Ви повинні додати a __eq__та __hash__метод у свій Dataклас, він може перевірити, чи є__dict__ атрибути (однакові властивості), а потім, чи рівні їх значення, теж.

Якщо ви зробили це, ви можете використовувати

test = Data()
test.n = 5

found = test in myList

У inключових словах перевіряє, testзнаходиться вmyList .

Якщо ви хочете лише мати nвластивість, Dataви можете використовувати:

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

Подумайте про використання словника:

myDict = {}

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

print(5 in myDict)

Або: d = dict ((i, i * i) для i в діапазоні (20))
hughdbrown

Це вирішує тривіальну проблему, яку я використовував для ілюстрації свого питання, але насправді не вирішив моє кореневе питання. Відповіддю, яку я шукав (понад 5 років тому), було розуміння списку. :)
m0j0

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.