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.