2011-08-19 13 views
92

Ho una lista di oggetti. Voglio trovare un oggetto (primo o qualsiasi altro) in questo elenco che ha attributo (o metodo risultato - qualunque) uguale a value.Trova oggetto in lista che ha attributo uguale ad un valore (che soddisfa qualsiasi condizione)

Qual è il modo migliore per trovarlo?

Ecco test case:

class Test: 
     def __init__(self, value): 
      self.value = value 

    import random 

    value = 5 

    test_list = [Test(random.randint(0,100)) for x in range(1000)] 

    # that I would do in Pascal, I don't believe isn't anywhere near 'Pythonic' 
    for x in test_list: 
     if x.value == value: 
      print "i found it!" 
      break 

penso utilizzando generatori e reduce() non farà alcuna differenza, perché ancora sarebbe scorrendo l'elenco.

ps .: l'equazione a value è solo un esempio. Certo, vogliamo ottenere elementi che soddisfino qualsiasi condizione.

+2

Ecco una buona discussione di questa domanda: http://tomayko.com/writings/cleanest-python-find -in-list-function –

+0

Il post originale è __ridiculously__ non aggiornato, ma la seconda risposta corrisponde esattamente alla mia versione a una riga.Non sono convinto che sia meglio della versione di base del ciclo. – agf

risposta

199
next((x for x in test_list if x.value == value), None) 

Questo diventa la prima voce dalla lista che coincide con la condizione, e restituisce None se nessun elemento corrisponde. È la mia forma preferita di espressione singola.

Tuttavia,

for x in test_list: 
    if x.value == value: 
     print "i found it!" 
     break 

La versione ingenua ciclo-break, è perfettamente Pythonic - è conciso, chiaro ed efficiente. Per farlo corrispondere al comportamento del one-liner:

for x in test_list: 
    if x.value == value: 
     print "i found it!" 
     break 
else: 
    x = None 

Questo assegnerà None-x se non break fuori dal giro.

+21

+1 per il rassicurante "La versione naive del loop-break, è perfettamente Pythonic". – LaundroMat

+0

ottima soluzione, ma come posso modificare la tua linea in modo che possa fare x.value in realtà significare x.fieldMemberName dove quel nome è memorizzato in valore? campo = "nome" successivo ((x per x in test_list se x.field == valore), Nessuno) modo che in questo caso, sono in realtà controllando contro x.name, non x.field –

+2

@StewartDale Non è del tutto chiaro cosa stai chiedendo, ma penso che tu intenda '... se getattr (x, x.fieldMemberName) == value'. Questo preleverà l'attributo da 'x' con il nome memorizzato in' fieldMemberName', e lo confronterà con 'value'. – agf

1

Ho appena incontrato un problema simile e messo a punto una piccola ottimizzazione per il caso in cui nessun oggetto nella lista soddisfa il requisito (per il mio caso d'uso questo ha determinato un miglioramento importante delle prestazioni):.

Insieme con la lista test_list, tengo un set set_value_set aggiuntivo che consiste in valori della lista su cui ho bisogno di filtrare. Quindi qui la parte else della soluzione di agf diventa molto veloce.

4

Poiché non è stato menzionato solo per il completamento. Il buon vecchio filtro per filtrare i tuoi elementi da filtrare.

Programmazione funzionale ftw.

####### Set Up ####### 
class X: 

    def __init__(self, val): 
     self.val = val 

elem = 5 

my_unfiltered_list = [X(1), X(2), X(3), X(4), X(5), X(5), X(6)] 

####### Set Up ####### 

### Filter one liner ### filter(lambda x: condition(x), some_list) 
my_filter_iter = filter(lambda x: x.val == elem, my_unfiltered_list) 
### Returns a flippin' iterator at least in Python 3.5 and that's what I'm on 

print(next(my_filter_iter).val) 
print(next(my_filter_iter).val) 
print(next(my_filter_iter).val) 

### [1, 2, 3, 4, 5, 5, 6] Will Return: ### 
# 5 
# 5 
# Traceback (most recent call last): 
# File "C:\Users\mousavin\workspace\Scripts\test.py", line 22, in <module> 
#  print(next(my_filter_iter).value) 
# StopIteration 


# You can do that None stuff or whatever at this point, if you don't like exceptions. 

So che in genere in python list comprehension sono preferiti o almeno questo è quello che ho letto, ma non vedo il problema per essere onesti. Ovviamente Python non è un linguaggio FP, ma Map/Reduce/Filter sono perfettamente leggibili e sono i più standard di casi d'uso standard nella programmazione funzionale.

Quindi eccoci. Conosci la tua programmazione funzionale.

elenco condizione di filtro

non otterrà più facile di così:

next(filter(lambda x: x.val == value, my_unfiltered_list)) # Optionally: next(..., None) or some other default value to prevent Exceptions 
+0

Mi piace lo stile di questo, ma ci sono due potenziali problemi. ** 1 **: Funziona solo in Python 3; in Python 2, 'filter' restituisce una lista che non è compatibile con' next'. ** 2 **: richiede una corrispondenza definita, altrimenti si otterrà un'eccezione di 'StopIteration'. – freethebees

+0

1: Non sono a conoscenza di Python 2. Quando ho iniziato ad usare Python, Python 3 era già disponibile. Sfortunatamente non ho idea degli specifci di Python 2. 2. @freethebees come sottolineato da agf. Puoi usare next (..., None) o qualche altro valore predefinito, se non sei fan delle eccezioni. L'ho anche aggiunto come commento al mio codice. – Nimi

+0

Ho aggiornato la risposta per riflettere i commenti. – Nimi

Problemi correlati