Rechercher une liste d'objets en Python

90

Supposons que je crée une classe simple pour fonctionner de manière similaire à une structure de style C, pour contenir uniquement des éléments de données. J'essaie de comprendre comment rechercher une liste d'objets pour les objets avec un attribut égal à une certaine valeur. Voici un exemple trivial pour illustrer ce que j'essaie de faire.

Par exemple:

class Data:
    pass

myList = []

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

Comment pourrais-je rechercher dans la liste myList pour déterminer si elle contient un élément avec n == 5?

J'ai cherché sur Google et recherché la documentation Python, et je pense que je pourrais peut-être le faire avec une compréhension de liste, mais je ne suis pas sûr. Je pourrais ajouter que je dois utiliser Python 2.4.3 en passant, donc toutes les nouvelles fonctionnalités de gee-whiz 2.6 ou 3.x ne sont pas disponibles pour moi.

m0j0
la source
Peut-être une bizarrerie involontaire de votre exemple: myList = [Data (). N == 0, Data (). N = 1, ...] où data.n serait assigné par range () et data.n serait le index dans myList. Par conséquent, vous permettant d'extraire n'importe quelle instance Data () simplement en référençant myList par une valeur d'index. Bien sûr, vous pourrez plus tard modifier myList [0] .n = 5.2 ou quelque chose. Et l'exemple était peut-être trop simplifié.
DevPlayer

Réponses:

131

Vous pouvez obtenir une liste de tous les éléments correspondants avec une compréhension de liste:

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

Si vous voulez simplement déterminer si la liste contient un élément qui correspond et le faire (relativement) efficacement, vous pouvez faire

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
la source
25
ou, any (custom_filter (x) for x in myList if xn == 30) qui est juste votre fonction "contient" comme fonction intégrée.
nosklo
Erreur de syntaxe sur nosklo - besoin d'un ensemble supplémentaire de () autour du générateur.
gahooa
Non. Essayez-le et voyez.
Robert Rossney
1
il serait bon de fusionner cette réponse avec celle de gahooa ( stackoverflow.com/a/598602/2349267 ).
Roman Hwang
76

Simple, élégant et puissant:

Une expression de générateur en conjonction avec un builtin… (python 2.5+)

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

Utilise la fonction any()intégrée Python , qui est définie comme suit:

any (iterable) -> Renvoie True si un élément de l'itérable est vrai. Équivalent à:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False
gahooa
la source
Agréable. Pour info, vous pouvez faire n'importe quel (x pour x dans ma liste si xn == 10) pour enregistrer des parenthèses (aussi == pas =).
Jacob Gabrielson
Je préfère utiliser any(x for x in mylist if x['n'] == 10)mais c'est une bonne idée
Alex Montoya
46

Juste pour être complet, n'oublions pas la chose la plus simple qui pourrait éventuellement fonctionner:

for i in list:
  if i.n == 5:
     # do something with it
     print "YAY! Found one!"
Charlie Martin
la source
38
[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
la source
1
C'est une bonne réponse en raison de la "première" méthode, qui est probablement le cas d'utilisation le plus courant.
galarant
grand merci! les indices de correspondance étaient ce que je recherchais. Existe-t-il un raccourci pour utiliser ceci pour indexer directement la liste pour accéder à un autre champ? Maintenant, j'obtiens une liste d'entrées de liste (il n'y a qu'une seule entrée, donc c'est une liste avec un élément). Pour obtenir l'index, je dois exécuter le résultat [0] avant de pouvoir l'utiliser pour indexer la liste. À partir de l'exemple de question, je veux accéder à n_squared à partir d'un n: myList [index of myList.n == 5] .n_squared
Frieke
31
filter(lambda x: x.n == 5, myList)
vartec
la source
25
pour quelqu'un qui veut apprendre Python, comprendre lambda est basique.
vartec
2
Eh bien, oui et non - avec la compréhension de liste et le tri des fonctions clés des fabricants comme operator.attrgetter, je n'utilise presque jamais lambdas.
Ben Hoyt
9

Vous pouvez utiliser inpour rechercher un élément dans une collection et une compréhension de liste pour extraire le champ qui vous intéresse. Cela (fonctionne pour les listes, les ensembles, les tuples et tout ce qui définit __contains__ou __getitem__).

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

Voir également:

Tom Dunham
la source
4

Vous devriez ajouter a __eq__et une __hash__méthode à votre Dataclasse, cela pourrait vérifier si les __dict__attributs sont égaux (mêmes propriétés) et ensuite si leurs valeurs sont égales aussi.

Si vous avez fait cela, vous pouvez utiliser

test = Data()
test.n = 5

found = test in myList

Le inmot-clé vérifie si testest dans myList.

Si vous ne voulez qu'une npropriété, Datavous pouvez utiliser:

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
la source
3

Pensez à utiliser un dictionnaire:

myDict = {}

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

print(5 in myDict)
dan-gph
la source
Ou: d = dict ((i, i * i) for i in range (20))
hughdbrown
Cela résout le problème trivial que j'ai utilisé pour illustrer ma question, mais n'a pas vraiment résolu ma question fondamentale. La réponse que je cherchais (il y a plus de 5 ans) était la compréhension de la liste. :)
m0j0
1

Une autre façon de procéder consiste à utiliser la fonction next ().

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