Durchsuchen einer Liste von Objekten in Python

89

Nehmen wir an, ich erstelle eine einfache Klasse, die ähnlich wie eine C-Struktur funktioniert und nur Datenelemente enthält. Ich versuche herauszufinden, wie eine Liste von Objekten nach Objekten mit einem Attribut durchsucht wird, das einem bestimmten Wert entspricht. Im Folgenden finden Sie ein einfaches Beispiel, um zu veranschaulichen, was ich versuche.

Zum Beispiel:

class Data:
    pass

myList = []

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

Wie würde ich die myList-Liste durchsuchen, um festzustellen, ob sie ein Element mit n == 5 enthält?

Ich habe gegoogelt und die Python-Dokumente durchsucht, und ich glaube, ich kann dies mit einem Listenverständnis tun, bin mir aber nicht sicher. Ich könnte hinzufügen, dass ich übrigens Python 2.4.3 verwenden muss, sodass mir keine neuen Funktionen von gee-whiz 2.6 oder 3.x zur Verfügung stehen.

m0j0
quelle
Vielleicht eine unbeabsichtigte Eigenart Ihres Beispiels: myList = [Data (). N == 0, Data (). N = 1, ...] wobei data.n durch range () zugewiesen wird und data.n das ist Index in myList. Daher können Sie jede Data () - Instanz aufrufen, indem Sie einfach auf myList durch einen Indexwert verweisen. Natürlich können Sie später myList [0] .n = 5.2 oder so ändern. Und das Beispiel wurde vielleicht zu stark vereinfacht.
DevPlayer

Antworten:

128

Sie können eine Liste aller übereinstimmenden Elemente mit einem Listenverständnis erhalten:

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

Wenn Sie einfach nur feststellen möchten, ob die Liste ein Element enthält , das übereinstimmt, und dies (relativ) effizient tun möchten , können Sie dies tun

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
Adam Rosenfield
quelle
25
oder eine beliebige (custom_filter (x) für x in myList, wenn xn == 30), die nur Ihre "enthält" -Funktion als integrierte Funktion ist.
Nosklo
Syntaxfehler auf nosklo - benötigt einen zusätzlichen Satz von () um den Generator.
Gahooa
Nicht so. Probieren Sie es aus und sehen Sie.
Robert Rossney
1
Es wäre gut, diese Antwort mit der von gahooa ( stackoverflow.com/a/598602/2349267 ) zusammenzuführen.
Roman Hwang
75

Einfach, elegant und kraftvoll:

Ein Generatorausdruck in Verbindung mit einem eingebauten… (Python 2.5+)

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

Verwendet das integrierte Python any(), das wie folgt definiert ist:

any (iterable) -> Gibt True zurück, wenn ein Element der iterable true ist. Gleichwertig:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False
gahooa
quelle
Nett. Zu Ihrer Information können Sie eine beliebige (x für x in meiner Liste, wenn xn == 10) tun, um einige Parens zu speichern (auch == nicht =).
Jacob Gabrielson
Ich bevorzuge die Verwendung any(x for x in mylist if x['n'] == 10), ist aber eine gute Idee
Alex Montoya
45

Vergessen wir der Vollständigkeit halber nicht die einfachste Sache, die möglicherweise funktionieren könnte:

for i in list:
  if i.n == 5:
     # do something with it
     print "YAY! Found one!"
Charlie Martin
quelle
37
[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
Markus Jarderot
quelle
1
Dies ist eine gute Antwort aufgrund der "ersten" Methode, die wahrscheinlich der häufigste Anwendungsfall ist.
Galarant
vielen Dank! Die Match-Indizes waren genau das, wonach ich gesucht habe. Gibt es eine Verknüpfung, mit der Sie die Liste direkt indizieren können, um auf ein anderes Feld zuzugreifen? Jetzt erhalte ich eine Liste mit Listeneinträgen (es gibt nur einen Eintrag, es handelt sich also um eine Liste mit einem Element). Um den Index zu erhalten, muss ich das Ergebnis [0] ausführen, bevor ich es zum Indizieren der Liste verwenden kann. Aus dem Fragenbeispiel möchte ich von einem bestimmten n: myList [Index von myList.n == 5] auf n_squared
zugreifen
31
filter(lambda x: x.n == 5, myList)
vartec
quelle
25
Für jemanden, der Python lernen möchte, ist das Verständnis von Lambda grundlegend.
vartec
2
Nun ja und nein - mit Listenverständnis und Sortieren von Schlüsselfunktionen wie operator.attrgetter verwende ich kaum lambdas.
Ben Hoyt
9

Sie können innach einem Element in einer Sammlung suchen und anhand eines Listenverständnisses das Feld extrahieren, an dem Sie interessiert sind. Dies (funktioniert für Listen, Mengen, Tupel und alles, was definiert __contains__oder definiert __getitem__).

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

Siehe auch:

Tom Dunham
quelle
4

Sie sollten Ihrer Klasse eine __eq__und eine __hash__Methode hinzufügen Data, um zu überprüfen, ob die __dict__Attribute gleich sind (gleiche Eigenschaften) und ob auch ihre Werte gleich sind.

Wenn Sie das getan haben, können Sie verwenden

test = Data()
test.n = 5

found = test in myList

Das inSchlüsselwort prüft, ob testin myList.

Wenn Sie nur eine nEigenschaft in möchten, können DataSie Folgendes verwenden:

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
Johannes Weiss
quelle
3

Verwenden Sie ein Wörterbuch:

myDict = {}

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

print(5 in myDict)
dan-gph
quelle
Oder: d = diktieren ((i, i * i) für i im Bereich (20))
hughdbrown
Es löst das triviale Problem, mit dem ich meine Frage illustriert habe, aber meine Grundfrage nicht wirklich gelöst hat. Die Antwort, nach der ich gesucht habe (vor mehr als 5 Jahren), war das Listenverständnis. :)
m0j0
0

Sie können dies auch mit der Funktion next () tun.

matched_obj = next(x for x in list if x.n == 10)
Oliver Breeden
quelle