2010-11-02 14 views
5

Dovrei prendere una lista di parole e contare tutte le parole in essa che sono 2 o più caratteri e dove il primo e l'ultimo carattere sono uguali.List comprehension e len() vs. simple for loop

mi si avvicinò con due possibili soluzioni:

result = 0 
for word in words: 
    if len(word) >= 2 and word[0] == word[-1]: 
     result += 1 
return result 

vs.

return len([word for word in words if len(word) >= 2 and word[0] == word[-1]]) 

quale sarebbe la soluzione preferita? O ci sono anche quelli migliori?

+0

Vorrei prendere # 2: ma formattato bene su più righe. Solo perché tu * puoi * scriverlo una riga non significa che dovresti (anche con generatori o compere, ecc.) –

+0

Per favore, per favore, per favore misura. Si prega di usare 'timeit' per misurare questo. Si prega di misurare e pubblicare i risultati. –

+1

@ S.Lott: non ha detto nulla sulle prestazioni. Il modo * preferito * di fare qualcosa non implica il modo * più veloce * di fare qualcosa. –

risposta

14

Nel secondo esempio uno generator expression sarebbe meglio di list-comp se l'elenco è di grandi dimensioni.

sum(1 for word in words if len(word) >= 2 and word[0] == word[-1]) 
+1

TypeError: oggetto di tipo 'generator' non ha len() – dan04

+0

Grazie! Stavo quasi per suggerirlo. – pyfunc

+0

@ dan04: grazie; risolto ora @pyfunc: grazie! – bernie

1

Entrambi sono abbastanza buoni.

ci sono piccole differenze:

di lista restituisce un'altra lista che si sta passando a len. La prima soluzione evita la creazione di un'altra lista.

3

Il primo sarebbe sicuramente la soluzione preferita in Python.

Non dimenticare il tuo Zen di Python:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren't special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess.

There should be one-- and preferably only one --obvious way to do it.

Although that way may not be obvious at first unless you're Dutch.

Now is better than never.

Although never is often better than right now.

If the implementation is hard to explain, it's a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea -- let's do more of those!

Diverso da quello che le soluzioni sono buone.

+4

La solita formula di zen-of-Python-pappagallo: fai un reclamo e indica un rigo come giustificazione, come se fosse un testo religioso. (Trovo che la seconda versione sia molto chiara, la prima sembra una cosa creata da C.) –

+0

Non sono affatto d'accordo. La comprensione delle liste è * molto * Pythonic e leggibile. –

2

Personalmente trovo il ciclo esplicito più leggibile, ma è una questione di gusti (alcuni preferiscono generalmente un codice più breve, soprattutto quando devono scriverlo).

versione entrambi i casi può essere ulteriormente accorciato/migliorata:

result = 0 
for word in words: 
    result += int(len(word) >= 2 and word[0] == word[-1]) 
return result 

L'int() le conversioni è propriamente inutili, in quanto vera è una sorta di 1, ma può essere meglio per migliorare la leggibilità. Lo stesso approccio può essere applicato alla comprensione:

return sum(len(word) >= 2 and word[0] == word[-1] for word in words) 

Se si desidera utilizzare len(), mi piacerebbe punto il lettore al fatto che i valori in realtà non importa:

len(1 for word in words if len(word) >= 2 and word[0] == word[-1]) 
+0

'a + = int (bool)' è unidiomatico al punto in cui lo chiamerei male. Non farlo; la condizione originale è molto più chiara. –

1

Altre varianti da prendere in considerazione:

Innanzitutto, è possibile suddividere la condizione del filtro in una funzione. Questa condizione va bene in entrambi i casi, ma se diventa più complesso mi piacerebbe sicuramente fare questo:

def check(word): 
    return len(word) >= 2 and word[0] == word[-1] 
sum(1 for word in words if check(word)) 

Poi, se la generazione di una lista (come nella lista di comprensione originale) è accettabile, allora si può fare questo :

len(filter(check, words)) 

C'è itertools.ifilter, ma se si utilizza che è necessario utilizzare di nuovo l'espressione sum, in modo che non finisce più chiaro.

Il trucco sum si presenta così spesso che sono sorpreso che non ci sia una chiamata di libreria standard per contare il numero di elementi in un iteratore (se c'è, non l'ho trovato). In alternativa, avrebbe senso se len consumasse e contasse il numero di voci in un iteratore se non ha __len__, ma non lo fa.