Come utilizzare il filtro, mappare e ridurre in Python 3


321

filter, mape reducefunziona perfettamente in Python 2. Ecco un esempio:

>>> def f(x):
        return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

>>> def cube(x):
        return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

>>> def add(x,y):
        return x+y
>>> reduce(add, range(1, 11))
55

Ma in Python 3, ricevo i seguenti output:

>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>

>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>

>>> reduce(add, range(1, 11))
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    reduce(add, range(1, 11))
NameError: name 'reduce' is not defined

Gradirei se qualcuno potesse spiegarmi perché questo è.

Schermata del codice per ulteriore chiarezza:

Sessioni IDLE di Python 2 e 3 affiancate


1
In breve, list non è l'unico tipo di dati. Se si desidera un elenco, dire che si desidera un elenco. Ma nella maggior parte dei casi, vuoi qualcos'altro comunque.
Veky,

Risposte:


346

Puoi leggere le modifiche in Novità di Python 3.0 . Dovresti leggerlo attentamente quando passi da 2.xa 3.x poiché molte cose sono state cambiate.

L'intera risposta qui è citazioni dalla documentazione.

Visualizzazioni e iteratori anziché elenchi

Alcune API ben note non restituiscono più elenchi:

  • [...]
  • map()e filter()restituisce iteratori. Se hai davvero bisogno di un elenco, una soluzione rapida è ad esempio list(map(...)), ma una soluzione migliore è spesso quella di utilizzare una comprensione dell'elenco (specialmente quando il codice originale usa lambda), o riscrivere il codice in modo che non abbia bisogno di un elenco. Viene particolarmente map()invocato per gli effetti collaterali della funzione; la trasformazione corretta è quella di utilizzare un forciclo regolare (poiché la creazione di un elenco sarebbe solo uno spreco).
  • [...]

builtins

  • [...]
  • Rimosso reduce(). Usa functools.reduce()se ne hai davvero bisogno; tuttavia, il 99 percento delle volte in cui un forciclo esplicito è più leggibile.
  • [...]

21
Aggiungendo list(map(...) ovunque .. come nel mondo sia aiutare la leggibilità .. pythonsembra che non riesca a gestire l'applicazione progressiva / streaming di combinatori funzionali. Altre lingue che posso concatenare una dozzina di operazioni contro una raccolta di fila ed è leggibile. Qui? cosa vuoi - una dozzina di modi annidati in??
javadba,

11
Se stai lavorando in un contesto imperativo, allora un for-loop è probabilmente l'opzione più leggibile. Ma ci sono buone ragioni per preferire un contesto funzionale - e rompere da quello per tornare alla procedura può essere davvero brutto.
MatrixManAtYrService il

2
@javadba Sei sicuro in una "applicazione di streaming" che devi aggiungere la listchiamata? Ho pensato che il significato di "streaming" sia "nessuna lista è stata creata affatto; elabora completamente ogni elemento dell'input prima di passare al successivo".
Imperishable Night,

@MatrixManAtYrService Se sei sicuro che il comportamento di Python 2 sia quello di cui hai bisogno, puoi sempre ridefinire map.
Imperishable Night,

6
Non riesco ancora a capire come un argomento di leggibilità porti a un simile cambiamento. Se fosse per motivi di performance potrei capire ...
Minato

86

La funzionalità di maped è filterstata intenzionalmente modificata per restituire gli iteratori e ridurre è stato rimosso dall'essere incorporato e inserito functools.reduce.

Quindi, per filtere map, puoi avvolgerli list()per vedere i risultati come hai fatto prima.

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> list(filter(f, range(2, 25)))
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> list(map(cube, range(1, 11)))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> import functools
>>> def add(x,y): return x+y
...
>>> functools.reduce(add, range(1, 11))
55
>>>

La raccomandazione ora è di sostituire l'uso della mappa e del filtro con espressioni di generatori o comprensioni di elenchi. Esempio:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> [i for i in range(2, 25) if f(i)]
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> [cube(i) for i in range(1, 11)]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>

Dicono che per i loop sono il 99 percento delle volte più facili da leggere che da ridurre, ma mi limiterei a attenermi functools.reduce.

Modifica : la cifra del 99 percento viene estratta direttamente dalla pagina Novità di Python 3.0 creata da Guido van Rossum.


5
Non è necessario creare funzioni extra nella comprensione dell'elenco. Basta usare[i*i*i for i in range(1,11)]
Xiao

2
Hai assolutamente ragione. Ho mantenuto la funzione negli esempi di comprensione dell'elenco per mantenerla simile agli esempi di filtro / mappa.
Joshua D. Boyd,

5
i ** 3 è anche equivalente a i * i * i
Breezer il

5
@Breezer in realtà i**3chiamerà i.__pow__(3)e i*i*i i.__mul__(i).__mul__(i)(o qualcosa del genere). Con ints non importa, ma con numeri intorpiditi / classi personalizzate potrebbe persino produrre risultati diversi.
sintomo

1
Ho notato che ogni volta che sentiamo che "Guido ha preso la decisione X" quel dolore è un probabile risultato. Questo è un ottimo esempio: list(list(list(.. )))fare ciò che era già prolisso in Python.
javadba,

12

Come addendum alle altre risposte, questo sembra un ottimo caso d'uso per un gestore di contesto che mapperà nuovamente i nomi di queste funzioni a quelli che restituiscono un elenco e introducono reducenello spazio dei nomi globale.

Una rapida implementazione potrebbe apparire così:

from contextlib import contextmanager    

@contextmanager
def noiters(*funcs):
    if not funcs: 
        funcs = [map, filter, zip] # etc
    from functools import reduce
    globals()[reduce.__name__] = reduce
    for func in funcs:
        globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
    try:
        yield
    finally:
        del globals()[reduce.__name__]
        for func in funcs: globals()[func.__name__] = func

Con un utilizzo simile al seguente:

with noiters(map):
    from operator import add
    print(reduce(add, range(1, 20)))
    print(map(int, ['1', '2']))

Che stampa:

190
[1, 2]

Solo i miei 2 centesimi :-)


1
pythoncome lingua è un pasticcio - ma ha v bene eccellenti librerie: numpy, pandas, statsmodelse gli amici .. mi era stato Buliding librerie di convenienza come si mostra qui per ridurre il dolore della lingua madre - ma hanno perso l'energia e non cercare di randagio lontano da a data.frame/ datatable, o xarray. Ma complimenti per aver provato ..
Javavba,

7

Poiché il reducemetodo è stato rimosso dalla funzione integrata da Python3, non dimenticare di importare functoolsnel tuo codice. Guarda lo snippet di codice qui sotto.

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)

2

Ecco alcuni esempi di funzioni di filtro, mappa e riduzione.

numeri = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]

//Filtro

oddNumbers = list (filtro (lambda x: x% 2! = 0, numeri))

PRINT (oddNumbers)

//Carta geografica

multiplyOf2 = list (mappa (lambda x: x * 2, numeri))

stampare (multiplyOf2)

//Ridurre

La funzione di riduzione, poiché non è comunemente usata, è stata rimossa dalle funzioni integrate in Python 3. È ancora disponibile nel modulo functools, quindi puoi fare:

da functools import ridurre

sumOfNumbers = ridurre (lambda x, y: x + y, numeri)

PRINT (sumOfNumbers)


0

Uno dei vantaggi di mappare, filtrare e ridurre è quanto diventano leggibili quando li "incateni" per fare qualcosa di complesso. Tuttavia, la sintassi integrata non è leggibile ed è tutta "arretrata". Quindi, suggerisco di usare il PyFunctionalpacchetto ( https://pypi.org/project/PyFunctional/ ). Ecco un confronto tra i due:

flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}

Versione PyFunctional

Sintassi molto leggibile. Si può dire:

"Ho una sequenza di destinazioni di volo. Da cui desidero ottenere la chiave dict se la città è nei valori di dict. Infine, filtra gli elenchi vuoti che ho creato nel processo."

from functional import seq  # PyFunctional package to allow easier syntax

def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
    return seq(flight_destinations_dict.items()) \
        .map(lambda x: x[0] if city in x[1] else []) \
        .filter(lambda x: x != []) \

Versione predefinita di Python

È tutto all'indietro. Devi dire:

"OK, quindi, c'è un elenco. Voglio filtrare elenchi vuoti da esso. Perché? Perché ho prima ottenuto la chiave dict se la città era nei valori di dict. Oh, l'elenco a cui sto facendo questo è flight_destinations_dict. "

def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
    return list(
        filter(lambda x: x != [],
               map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
               )
    )
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.