2009-02-28 7 views
50

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

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 aller sur la recherche dans la liste myList pour déterminer si elle contient un élément avec n == 5?

J'ai été googling et recherche les docs Python, et je pense que je pourrais être en mesure de le faire avec une compréhension de la liste, mais je ne suis pas sûr. J'ajouterai que je dois utiliser Python 2.4.3 en passant, donc les nouvelles fonctionnalités de gee-whiz 2.6 ou 3.x ne sont pas disponibles pour moi.

+0

Peut-être une bizarrerie non intentionnelle de votre exemple:. MyList = n [Data() == 0, (Data) n = 1. , ...] où data.n serait assigné par range() et data.n serait l'index dans myList. Vous permettant ainsi de remonter n'importe quelle instance Data() en référençant myList par une valeur d'index. Bien sûr, vous pouvez plus tard modifier myList [0] .n = 5.2 ou quelque chose. Et l'exemple était peut-être trop simplifié. – DevPlayer

Répondre

66

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

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

Si vous simplement vouloir déterminer si la liste contient un élément qui correspond et le faire (relativement) efficacement, vous pouvez le 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 
+19

ou, any (custom_filter (x) pour x dans myList si x.n == 30) qui est juste votre "contient" fonction comme un builtin. – nosklo

+0

Erreur de syntaxe sur nosklo - nécessite un jeu supplémentaire de() autour du générateur. – gahooa

+0

Pas si. Essayez-le et voyez. –

1

Vous devez ajouter un __eq__ et une méthode __hash__ à votre Data classe, il pourrait vérifier si les __dict__ attributs sont égaux (mêmes propriétés) et si leurs valeurs sont égales, aussi.

Si vous avez fait cela, vous pouvez utiliser

test = Data() 
test.n = 5 

found = test in myList 

Les chèques de mot-clé in si test est en myList.

Si vous voulez seulement aa propriété n à Data vous 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 
25
[x for x in myList if x.n == 30]    # list of all 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 
+0

C'est une bonne réponse à cause de la "première" méthode, qui est probablement le cas d'utilisation le plus courant. – galarant

36

Juste pour être complet, il ne faut pas oublier la chose la plus simple qui pourrait travail:

for i in list: 
    if i.n == 5: 
    # do something with it 
    print "YAY! Found one!" 
24
filter(lambda x: x.n == 5, myList) 
+23

pour quelqu'un qui veut apprendre Python, comprendre lambda est basique. – vartec

+1

Eh bien, oui et non - avec les listes de compréhension et de tri des fonctions clés des décideurs comme operator.attrgetter, je n'utilise presque jamais de 'lambda's. –

7

Vous pouvez utiliser in pour rechercher un élément dans une collection, et liste de compréhension pour extraire le champ qui vous intéresse. Ceci (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 aussi:

46

Simple, élégant et puissant:

Une expression du générateur en conjonction avec une commande interne ... (pyt L'honorable 2.5+)

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

Utilise le Python any() builtin, qui est défini comme suit:

tout (itérables)-> Retour Vrai si un élément du itérables est vrai. Équivalent à:

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

Nice. FYI vous pouvez en faire (x pour x dans mylist si x.n == 10) pour sauver quelques parens (aussi == not =). –

3

Tenir compte en utilisant un dictionnaire:

myDict = {} 

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

print(5 in myDict) 
+0

Ou: d = dict ((i, i * i) pour i dans gamme (20)) – hughdbrown

+0

Il résout le problème trivial que j'ai utilisé pour illustrer ma question, mais n'a pas vraiment résolu ma question racine. La réponse que je cherchais (il y a plus de 5 ans) était la compréhension de la liste. :) – m0j0