C'è un valore nell'usare map() vs for?
-
22-08-2019 - |
Domanda
map() scorre l'elenco come farebbe "for"?C'è un valore nell'usare map vs for?
Se è così, in questo momento il mio codice è simile a questo:
for item in items:
item.my_func()
Se ha senso, vorrei renderlo map().È possibile?Com'è un esempio?
Soluzione
Potresti usare map
invece del for
loop che hai mostrato, ma dal momento che non sembra che tu utilizzi il risultato di item.my_func()
, questo è non consigliato. map
dovrebbe essere usato se vuoi applicare una funzione senza effetti collaterali a tutti gli elementi di un elenco.In tutte le altre situazioni, utilizzare un ciclo for esplicito.
Inoltre, a partire da Python 3.0 map
restituisce un generatore, quindi in quel caso map
non si comporterà allo stesso modo (a meno che non valuti esplicitamente tutti gli elementi restituiti dal generatore, ad es.a chiamata list
su di essa).
Modificare: kibibu chiede nei commenti un chiarimento sul perché map
Il primo argomento di non dovrebbe essere una funzione con effetti collaterali.Proverò a rispondere a questa domanda:
map
è pensato per passare una funzione f
in senso matematico.In tali circostanze non importa in quale ordine f
viene applicato agli elementi del secondo argomento (purché siano restituito nel loro ordine originale, ovviamente).Ancora più importante, in quelle circostanze map(g, map(f, l))
è semanticamente equivalente a map(lambda x: g(f(x)), l)
, indipendentemente dall'ordine in cui f
E g
vengono applicati ai rispettivi input.
Ad esempio, non importa se map
restituisce e iteratore o un elenco completo contemporaneamente.Tuttavia, se f
e/o g
causare effetti collaterali, allora questa equivalenza è garantita solo se la semantica di map(g, map(f, l))
sono tali che in qualsiasi fase g
viene applicato al primo N elementi restituiti da map(f, l)
Prima map(f, l)
si applica f
al (n+1)primo elemento di l
.(Intendendo che map
deve eseguire l'iterazione più pigra possibile, cosa che fa in Python 3, ma non in Python 2!)
Fare un ulteriore passo avanti:anche se assumiamo l'implementazione di Python 3 di map
, l'equivalenza semantica potrebbe facilmente fallire se l'output di map(f, l)
è ad es.attraversato itertools.tee
prima di essere fornito all'esterno map
chiamata.
La discussione di cui sopra può sembrare di natura teorica, ma man mano che i programmi diventano più complessi, diventa più difficile ragionarci su e quindi più difficile eseguirne il debug.Garantire che alcune cose siano invarianti allevia in qualche modo il problema e può di fatto prevenire un'intera classe di bug.
Da ultimo, map
ricorda a molte persone la sua controparte veramente funzionale in vari linguaggi (puramente) funzionali.Passargli una "funzione" con effetti collaterali confonderà quelle persone.Pertanto, vedere come alternativa (cioè utilizzare un ciclo esplicito) non è più difficile da implementare rispetto a una chiamata a map
, si consiglia vivamente di limitarne l'uso map
a quei casi in cui la funzione da applicare non provoca effetti collaterali.
Altri suggerimenti
È possibile scrivere questo usando la mappa in questo modo:
map(cls.my_func, items)
sostituendo CLS con la classe degli elementi che si effettua l'iterazione.
Come già detto da Stephan202, questo è non raccomandato in questo caso.
Come regola generale, se si vuole creare un nuovo elenco, applicando una funzione ad ogni elemento della lista, utilizzare carta. Questo ha comportato la il che significa che la funzione non ha alcun effetto collaterale, e quindi si potrebbe (potenzialmente) eseguire la mappa in parallelo.
Se non si desidera creare un nuovo elenco, o se la funzione ha effetti collaterali, utilizzare un ciclo for. Questo è il caso nel tuo esempio.
C'è una leggera differenza semantica, che probabilmente è chiuso nelle specifiche linguaggio Python. Il mappa è esplicitamente parallelizzabile, mentre per solo in situazioni particolari. Il codice può rompere dalla per , ma solo scappare con l'eccezione di mappa .
A mio parere mappa non dovrebbe garantire anche ordine di applicazione, mentre la funzione per must. AFAIK nessuna implementazione pitone è attualmente in grado di fare questo auto-parallelizzazione.
È possibile passare il tuo map
a qualche quadro fresco filettata o multiprocessing O calcolo distribuito se è necessario. Disco è un esempio di distribuzione resistenti ai guasti quadro basato, erlang-e-pitone. Ho configurato su 2 scatole di 8 core e ora il mio programma viene eseguito 16 volte più veloce, grazie al cluster Disco, però ho dovuto riscrivere il mio programma da list comprehension e cicli for a Map / Reduce.
E 'lo stesso accordo per scrivere un programma utilizzando per i loop e list comprehension e mappa / ridurre, ma quando ne avete bisogno per funzionare su un cluster, si può fare quasi gratis se si è utilizzato mappa / ridurre. Se non l'hai fatto, bene, si dovrà riscrivere.
Attenzione: per quanto ne so, python 2.x restituisce un elenco invece di un iteratore dalla mappa. Ho sentito questo può essere aggirato utilizzando iter.imap()
(mai usato però).
Utilizzare un esplicita per-ciclo quando non hai bisogno di una lista di risultati indietro (ad es. Le funzioni con effetti collaterali).
Utilizzare un elenco di comprensione quando si ha bisogno di un elenco di risultati di nuovo (per es. Le funzioni che restituiscono un valore basato direttamente sull'ingresso).
Usa mappa () quando si sta cercando di convincere gli utenti Lisp che Python vale la pena utilizzare. ;)
Il vantaggio principale di map
è quando vuoi ottenere il risultato di alcuni calcoli su ogni elemento di una lista.Ad esempio, questo snippet raddoppia ogni valore in un elenco:
map(lambda x: x * 2, [1,2,3,4]) #=> [2, 4, 6, 8]
È importante notarlo map
restituisce un nuovo elenco con i risultati.Non modifica l'elenco originale in vigore.
Per fare la stessa cosa con for
, dovresti creare un elenco vuoto e aggiungere una riga aggiuntiva al file for
body per aggiungere il risultato di ciascun calcolo al nuovo elenco.IL map
la versione è più concisa e funzionale.
Mappa a volte può essere più veloce per le funzioni built-in di codifica manualmente un ciclo for. Prova di temporizzazione mappa (str, gamma (1000000)) a fronte di un simile ciclo for.
map(lambda item: item.my_func(), items)