Przeszukiwanie listy obiektów w Pythonie


94

Załóżmy, że tworzę prostą klasę, która będzie działać podobnie do struktury w stylu C, aby po prostu przechowywać elementy danych. Próbuję dowiedzieć się, jak przeszukać listę obiektów dla obiektów z atrybutem równym określonej wartości. Poniżej znajduje się trywialny przykład ilustrujący, co próbuję zrobić.

Na przykład:

class Data:
    pass

myList = []

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

Jak mam przejść do przeszukiwania listy myList, aby określić, czy zawiera ona element z n == 5?

Szukałem w Google i przeszukiwałem dokumentację Pythona i myślę, że mógłbym to zrobić ze zrozumieniem listy, ale nie jestem pewien. Mógłbym dodać, że przy okazji muszę używać Pythona 2.4.3, więc żadne nowe funkcje gee-whiz 2.6 lub 3.x nie są dla mnie dostępne.


Być może niezamierzone dziwactwo twojego przykładu: myList = [Data (). N == 0, Data (). N = 1, ...], gdzie data.n zostanie przypisana przez range (), a data.n będzie indeks do myList. W związku z tym umożliwiając pobranie dowolnej instancji Data () po prostu odwołując się do myList za pomocą wartości indeksu. Oczywiście możesz później zmodyfikować myList [0] .n = 5.2 czy coś. A przykład był być może nadmiernie uproszczony.
— DevPlayer

Odpowiedzi:


139

Możesz uzyskać listę wszystkich pasujących elementów ze zrozumieniem listy:

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

Jeśli chcesz po prostu określić, czy lista zawiera dowolny element, który pasuje i zrobić to (względnie) wydajnie, możesz to zrobić

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
lub dowolna (custom_filter (x) for x w myList, jeśli xn == 30), która jest po prostu twoją funkcją „zawiera” jako wbudowaną.
— nosklo

Błąd składni w nosklo - potrzebny jest dodatkowy zestaw () wokół generatora.
— gahooa

Bynajmniej. Spróbuj i zobacz.
— Robert Rossney

1
dobrze byłoby połączyć tę odpowiedź z gahooa ( stackoverflow.com/a/598602/2349267 ).
— Roman Hwang,

77

Prosty, elegancki i potężny:

Wyrażenie generatora w połączeniu z wbudowanym… (python 2.5+)

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

Używa any()wbudowanego języka Python , który jest zdefiniowany w następujący sposób:

any (iterowalne) -> Zwraca True, jeśli jakikolwiek element iterowalny jest prawdziwy. Równoważny:

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

Ładny. FYI, możesz zrobić dowolne (x dla x w mylist, jeśli xn == 10), aby zapisać kilka par (również == not =).
— Jacob Gabrielson

Wolę używać, any(x for x in mylist if x['n'] == 10)ale to dobry pomysł
— Alex Montoya,

47

Dla kompletności nie zapominajmy o najprostszej rzeczy, która mogłaby zadziałać:

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
Jest to dobra odpowiedź ze względu na metodę „pierwszą”, która jest prawdopodobnie najczęstszym przypadkiem użycia.
— galarant

wielkie dzięki! indeksy meczowe były tym, czego szukałem. Czy istnieje skrót do bezpośredniego indeksowania listy w celu uzyskania dostępu do innego pola? Teraz otrzymuję listę wpisów na liście (jest tylko jeden wpis, więc jest to lista z jednym elementem). Aby uzyskać indeks, muszę wykonać wynik [0], zanim będę mógł go użyć do indeksowania listy. Z przykładu pytania chcę uzyskać dostęp do n_squared z konkretnego n: myList [indeks myList.n == 5] .n_squared
— Frieke

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

25
dla kogoś, kto chce się uczyć Pythona, zrozumienie lambdy jest podstawą.
— vartec

2
Cóż, tak i nie - dzięki składaniu list i sortowaniu kluczowych funkcji, takich jak operator.attrgetter, prawie nigdy nie używam lambdas.
— Ben Hoyt,

9

Możesz użyć indo wyszukania elementu w kolekcji i listy złożonej, aby wyodrębnić pole, które Cię interesuje. To (działa dla list, zbiorów, krotek i wszystkiego, co definiuje __contains__lub __getitem__).

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

Zobacz też:


4

Powinieneś dodać a __eq__i __hash__metodę do swojej Dataklasy, może ona sprawdzić, czy __dict__atrybuty są równe (te same właściwości), a następnie, czy ich wartości są równe.

Jeśli to zrobiłeś, możesz użyć

test = Data()
test.n = 5

found = test in myList

Słowo inkluczowe sprawdza, czy testjest w myList.

Jeśli chcesz mieć tylko nnieruchomość w domu, Datamożesz użyć:

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

Rozważ użycie słownika:

myDict = {}

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

print(5 in myDict)

Lub: d = dict ((i, i * i) for i in range (20))
— hughdbrown

Rozwiązuje trywialny problem, którego użyłem do zilustrowania mojego pytania, ale tak naprawdę nie rozwiązał mojego głównego pytania. Odpowiedzią, której szukałem (ponad 5 lat temu) było zrozumienie listy. :)
— m0j0

1

Innym sposobem, w jaki możesz to zrobić, jest użycie funkcji next ().

matched_obj = next(x for x in list if x.n == 10)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.