Hai chiesto quale era più efficiente. Supponendo che tu stia parlando della velocità di esecuzione: se i tuoi dati sono piccoli, non importa. Se è grande e tipico, il caso "esiste già" avverrà molto più spesso del caso "non in dict". Questa osservazione spiega alcuni dei risultati.
Di seguito è riportato un codice che può essere utilizzato con il modulo timeit
per esplorare la velocità senza sovraccarico di lettura file. Mi sono permesso di aggiungere un quinto metodo, che non è incompleto e verrà eseguito su qualsiasi Python da almeno 1.5.2 [testato] in poi.
from collections import defaultdict, Counter
def tally0(iterable):
# DOESN'T WORK -- common base case for timing
d = {}
for item in iterable:
d[item] = 1
return d
def tally1(iterable):
d = {}
for item in iterable:
if item in d:
d[item] += 1
else:
d[item] = 1
return d
def tally2(iterable):
d = {}
for item in iterable:
try:
d[item] += 1
except KeyError:
d[item] = 1
return d
def tally3(iterable):
d = defaultdict(int)
for item in iterable:
d[item] += 1
def tally4(iterable):
d = Counter()
for item in iterable:
d[item] += 1
def tally5(iterable):
d = {}
dg = d.get
for item in iterable:
d[item] = dg(item, 0) + 1
return d
tipico run (in Windows XP "Prompt dei comandi" finestra):
prompt>\python27\python -mtimeit -s"t=1000*'now is the winter of our discontent made glorious summer by this son of york';import tally_bench as tb" "tb.tally1(t)"
10 loops, best of 3: 29.5 msec per loop
Ecco i risultati (msec per anello):
0 base case 13.6
1 if k in d 29.5
2 try/except 26.1
3 defaultdict 23.4
4 Counter 79.4
5 d.get(k, 0) 29.2
Un altro studio tempistica:
prompt>\python27\python -mtimeit -s"from collections import defaultdict;d=defaultdict(int)" "d[1]+=1"
1000000 loops, best of 3: 0.309 usec per loop
prompt>\python27\python -mtimeit -s"from collections import Counter;d=Counter()" "d[1]+=1"
1000000 loops, best of 3: 1.02 usec per loop
La velocità di Counter
è probabilmente dovuto al fatto che è stato implementato in parte in codice Python mentre defaultdict
è interamente in C (in 2.7, almeno).
noti che Counter()
non è solo "zucchero sintattico" per defaultdict(int)
- si attua una completa bag
aka multiset
oggetto - vedi la documentazione per i dettagli; potrebbero salvarti dal reinventare la ruota se hai bisogno di qualche elaborata post-elaborazione. Se tutto quello che vuoi è contare le cose, usa defaultdict
.
Aggiornamento in risposta ad una domanda da @Steven Rumbalski: """ Sono curioso, che cosa succede se si sposta il iterabile nel costruttore Contatore:? D = Counter (iterable) (ho Python 2.6 e non può testarlo) """
tally6:. semplicemente non d = Count(iterable); return d
, prende 60.0 msec
si potrebbe guardare alla fonte (collections.py nel repository SVN) ... ecco cosa mia Python27\Lib\collections.py
fa quando iterable
non è un'istanza di mappatura:
self_get = self.get
for elem in iterable:
self[elem] = self_get(elem, 0) + 1
Hai visto quel codice prima? C'è molto bagaglio a mano solo per chiamare il codice eseguibile in Python 1.5.2 :-O
Ulteriori modifiche: Sto eseguendo la versione 2.7. Dovrebbe averlo aggiunto prima! –
'Counter' ** è più lento ** ma ha molte più funzionalità di' defaultdict (int) '- guarda la mia risposta. –
è possibile aumentare più valori? – Mohsin