2010-08-03 15 views
12

Sto cercando una soluzione migliore/più Pythonic per il seguente frammentoCome contare elementi non nulli in un iterabile?

count = sum(1 for e in iterable if e) 
+7

cosa c'è di sbagliato o "unpitano" a modo tuo? –

+1

Le espressioni del generatore sono Pythonic. Vuoi solo dire più breve? –

+0

OP, essere consapevoli di ciò che è e non è non nullo, in Python. Per esempio 'sum (1 per e in [False, 0, '', [],(), [''], (''), [False], (False,), Nessuno] se e)' valuta effettivamente a 3 invece di 0, perché stranamente le sequenze nidificate '[['']]', '[[False]]', '[(False,)]' contano come non nulli, tuttavia '[('')] "no, [! In ogni caso, in generale, il codice che date è soddisfacente e adeguato al compito, se siamo sicuri che la lista è piatta. – smci

risposta

18
len(filter(None, iterable)) 

Utilizzando None come il predicato per filter dice solo di utilizzare la truthiness degli elementi. (Forse più chiara sarebbe len(filter(bool, iterable)))

+0

+1 Questo è bello e veloce (circa 8 volte più veloce dell'espressione generatore sul mio computer). 'bool' sembra essere leggermente più lento di' None' –

+10

Usa memoria O (N) extra. –

+1

Soluzione rapida, ma la memoria extra O (N) è un segno negativo. La mia lista di solito ha 1000000 elementi, quindi usando 1MB di memoria solo per contare elementi diversi da zero non è così efficiente. Inoltre, non penso che tu abbia cronometrato il gc per recuperare l'elenco generato dal filtro. – Alexandru

3

Onestamente, non riesco a pensare ad un modo migliore per farlo che quello che hai.

Beh, credo che la gente potrebbe discutere sulla "migliore", ma penso che tu sia improbabile trovare qualcosa di più breve, più semplice, e più chiara.

+0

Sì. Grazie. – ars

0

Questo non è il più veloce, ma forse a portata di mano per il codice-golf

sum(map(bool, iterable)) 
+1

Utilizza memoria aggiuntiva O (N). –

+0

Solo in Python 2.x - in Python 3, map restituisce un generatore, non una lista. – PaulMcG

+0

Inoltre puoi usare imap da itertools. –

2
sum(not not e for e in iterable) 
+1

Wow. Questa risposta è abbastanza vecchia che 'bool (e)' non esisteva. –

3

La maggior parte Pythonic è quello di scrivere una piccola funzione ausiliaria e metterlo nel vostro fidato modulo "di pubblica utilità" (o modulo di pacchetto appropriato, quando si hanno abbastanza ;-):

import itertools as it 

def count(iterable): 
    """Return number of items in iterable.""" 
    return sum(1 for _ in iterable) 

def count_conditional(iterable, predicate=None): 
    """Return number of items in iterable that satisfy the predicate.""" 
    return count(it.ifilter(predicate, iterable)) 

Esattamente come si sceglie di implementare ° Queste utility sono meno importanti (si può scegliere in qualsiasi momento di ricodificarne alcune in Cython, ad esempio, se qualche profilazione su un'applicazione che utilizza le utility mostra che è utile): la cosa fondamentale è averli come propria utile libreria di utilità funzioni, con nomi e modelli di chiamata si piace, per rendere il vostro tutto-importante applicazione codice di livello più chiaro, più leggibile e più conciso che se si ficcò pieno di contorsioni inline -!)

+0

"calling patterns [that] you like": ci si può aspettare che i lettori di codice non-autore sappiano cosa (per esempio) 'sum (map (bool, iterable))' fa senza chiedere SO o cercare TFM ... avendo per trovare una definizione altrove, si interrompe il flusso di lettura. –

0

Se si' solo cercando di vedere se l'iterabile non è vuoto, allora questo probabilmente aiuterebbe:

def is_iterable_empty(it): 
    try: 
     iter(it).next() 
    except StopIteration: 
     return True 
    else: 
     return False 

Il altre risposte impiegheranno O (N) tempo per completare (e alcuni prenderanno memoria O (N); buon occhio, John!). Questa funzione richiede O (1) tempo. Se hai davvero bisogno della lunghezza, le altre risposte ti aiuteranno di più.

+0

Ummm bene (1) questo sarebbe più corto: 'is_iterable_empty = lambda it: not any (it)' (2) Questo non consuma il primo elemento se l'iteratore non è vuoto ed è un iteratore? –

+0

In realtà, la tua soluzione con la funzione 'any' in realtà non farà la stessa cosa in tutti i casi. Se l'iteratore restituisce solo oggetti booleani '' False' '(o liste vuote, ecc.), Allora il tuo lambda darebbe un falso positivo. Sono sicuro che questo codice potrebbe essere abbreviato, ma mi piace dare codice espanso per le dimostrazioni. Per quanto riguarda il consumo del primo oggetto, sì, lo sarà, ma tutte le soluzioni qui consumeranno almeno un elemento (la maggior parte consuma tutto). Non c'è un buon modo per aggirare questo problema, ma finché è documentato, allora tutto dovrebbe andare bene. –

0

Propably il modo più Pythonic è quello di scrivere codice che non ha bisogno di funzione di conteggio.

solito più veloce è quello di scrivere lo stile di funzioni che si sono meglio con e continuare a perfezionare il tuo stile.

Write Once Read Spesso codice.

A proposito il tuo codice non fa quello che dice il tuo titolo! Per contare non 0 elementi non è semplice considerando gli errori di arrotondamento nei numeri fluttuanti, che False è 0 ..

Se non avete valori in virgola mobile in elenco, questo potrebbe farlo:

def nonzero(seq): 
    return (item for item in seq if item!=0) 

seq = [None,'', 0, 'a', 3,[0], False] 
print seq,'has',len(list(nonzero(seq))),'non-zeroes' 

print 'Filter result',len(filter(None, seq)) 

"""Output: 
[None, '', 0, 'a', 3, [0], False] has 5 non-zeroes 
Filter result 3 
""" 
+0

No, non è così, è solo un modo complicato di dire 'len (lista (elemento per oggetto in seq se item! = 0))'. In realtà è ** meno Pythonic **. Non c'è bisogno di 'nonzero (seq)' per costruire l'elenco intermedio usa e getta quando tutto ciò che vogliamo fare è contare 1 per se un oggetto è diverso da zero. Farlo in un'espressione di generatore significa che non abbiamo bisogno di costruire una lista intermedia, AFAIK. – smci

0

Ecco un soluzione con O (n) runtime e O (1) memoria aggiuntiva:

count = reduce(lambda x,y:x+y, imap(lambda v: v>0, iterable)) 

Sperare che aiuti!

Problemi correlati