2012-06-01 4 views
18

lascia supporre la seguente semplice oggetto:trovare l'oggetto dal suo membro all'interno di una lista in Python

class Mock: 
    def __init__(self, name, age): 
     self.name = name 
     self.age = age 

poi ho una lista con alcuni oggetti di questo tipo:

myList = [Mock("Dan", 34), Mock("Jack", 30), Mock("Oli", 23)...] 

c'è qualche incorporato in funzione in cui posso ottenere tutti i mazzi con un'età di cioè 30? Certo che posso iterare me stesso su di loro e confrontare i loro età, ma qualcosa di simile

find(myList, age=30) 

sarebbe bello. C'è qualcosa del genere?

risposta

32

Si potrebbe desiderare di pre-index loro -

from collections import defaultdict 

class Mock(object): 
    age_index = defaultdict(list) 

    def __init__(self, name, age): 
     self.name = name 
     self.age = age 
     Mock.age_index[age].append(self) 

    @classmethod 
    def find_by_age(cls, age): 
     return Mock.age_index[age] 

Edit: una foto vale più di mille parole:

enter image description here

X axis è il numero di Mocks in myList, l'asse Y è runtime in secondi.

  • puntini rossi sono @ filtro di dcrooney metodo()
  • puntini blu sono @ di lista di marshall.ward
  • punti verdi che si nascondono dietro l'asse X sono il mio indice ;-)
+0

Grazie mille per questa risposta. Stavo per reimplementare qualcosa di simile manualmente. – xlash

+2

Grazie.E bel diagramma. Ma sarebbe utile discutere dei vari compromessi, ad es. tempo di avvio aggiuntivo per creare l'indice e lo spazio necessario per esso. – nealmcb

34

Si potrebbe provare un filter():

filter(lambda x: x.age == 30, myList) 

Questo sarebbe restituire una lista con solo gli oggetti che soddisfa l'espressione lambda.

+3

E se avete intenzione di fare una ricerca da questo attributo spesso, si consiglia di mantenere un 'dict' base all'attributo. –

+1

Almeno per Python 3.5, è necessario 'list (filter (lambda x: x.age == 30, myList))' per ottenere una lista. – CGFoX

+1

L'uso di 'filter()' è la vecchia risposta classica, ma la comprensione delle liste è più veloce e più portabile tra le versioni di Python, come discusso in altre risposte. – nealmcb

19

di lista può prenderli:

new_list = [x for x in myList if x.age == 30] 
+0

bella soluzione, ma la risposta @dcrooney si adatta meglio alla funzione. Qualcuno ha confrontato cosa è più veloce? –

+1

'timeit' fornisce circa 0,30 usec per la comprensione delle liste e 0,58 per il filtro lambda sulla mia macchina. Ma qui sono essenzialmente equivalenti, quindi dovresti usare quello che preferisci. –

+2

Anche qui buona discussione del problema: http://stackoverflow.com/a/1247490/317172 –

5

Lista le comprensioni sono quasi sempre il modo più veloce per fare queste cose (2x come veloce qui), anche se come accennato in precedenza l'indicizzazione è ancora più veloce se sei preoccupato per la velocità.

~$ python -mtimeit -s"from mock import myList" "filter(lambda x: x.age==21, myList)" 
1000000 loops, best of 3: 1.34 usec per loop 
~$ python -mtimeit -s"from mock import myList" "[x for x in myList if x.age==21]" 
1000000 loops, best of 3: 0.63 usec per loop 

Per file di mock.py nella directory corrente:

class Mock: 
    def __init__(self, name, age): 
     self.name = name 
     self.age = age 

myList = [Mock('Tom', 20), Mock('Dick', 21), Mock('Harry', 21), Mock('John', 22)] 
Problemi correlati