Cómo usar filtrar, mapear y reducir en Python 3
Así es como estoy acostumbrado a filter
trabajar map
en reduce
Python 2:
>>> 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
Sin embargo, todo esto parece fallar en Python 3:
>>> 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
¿Por qué los resultados son diferentes? ¿Cómo puedo hacer que el código Python 3 funcione como lo hizo el código Python 2?
Ver también: ¿ Cuál es el problema con reducir()? para obtener una motivación específica para que el cambio se coloque reduce
en un módulo de biblioteca estándar en lugar de dejarlo como incorporado.
Consulte Obtener un mapa() para devolver una lista en Python 3.x para obtener respuestas más específicas sobre map
.
Puede leer sobre los cambios en Novedades de Python 3.0 . Debería leerlo detenidamente cuando pase de 2.xa 3.x, ya que se han cambiado muchas cosas.
La respuesta completa aquí son citas de la documentación.
Vistas e iteradores en lugar de listas
Algunas API conocidas ya no devuelven listas:
- [...]
map()
yfilter()
devolver iteradores. Si realmente necesita una lista, una solución rápida es, por ejemplolist(map(...))
, pero una mejor solución suele ser usar una lista por comprensión (especialmente cuando el código original usa lambda) o reescribir el código para que no necesite una lista en absoluto.map()
Los efectos secundarios de la función son especialmente complicados ; la transformación correcta es utilizar unfor
bucle regular (ya que crear una lista sería un desperdicio).- [...]
Incorporaciones
- [...]
- Eliminado
reduce()
. Úselofunctools.reduce()
si realmente lo necesita; sin embargo, el 99 por ciento de las veces unfor
bucle explícito es más legible.- [...]
La funcionalidad de map
y filter
se cambió intencionalmente para devolver iteradores, y reducir se eliminó de ser una función integrada y se colocó en functools.reduce
.
Entonces, para filter
y map
, puedes envolverlos list()
para ver los resultados como lo hiciste antes.
>>> 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 recomendación ahora es que reemplace el uso de mapas y filtros con expresiones generadoras o listas por comprensión. Ejemplo:
>>> 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]
>>>
Dicen que los bucles for son el 99 por ciento de las veces más fáciles de leer que de reducir, pero yo me quedaría con functools.reduce
.
Editar : La cifra del 99 por ciento se extrae directamente de la página Novedades de Python 3.0 escrita por Guido van Rossum.
Como complemento a las otras respuestas, esto suena como un buen caso de uso para un administrador de contexto que reasignará los nombres de estas funciones a aquellas que devuelven una lista y las introducen reduce
en el espacio de nombres global.
Una implementación rápida podría verse así:
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 uso que se parece a este:
with noiters(map):
from operator import add
print(reduce(add, range(1, 20)))
print(map(int, ['1', '2']))
Que imprime:
190
[1, 2]
Sólo mis 2 centavos :-)