python3 mapped funcion from array python list-comprehension map-function

funcion - python mapped



Python List Comprehension Vs. Mapa (10)

Debe usar el map y el filter lugar de las listas de comprensión.

Una razón objetiva por la que deberías preferirlos aunque no sean "Pythonic" es esta:
Requieren funciones / lambdas como argumentos, que introducen un nuevo alcance .

Me he mordido por esto más de una vez:

for x, y in somePoints: # (several lines of code here) squared = [x ** 2 for x in numbers] # Oops, x was silently overwritten!

Pero si por el contrario hubiera dicho:

for x, y in somePoints: # (several lines of code here) squared = map(lambda x: x ** 2, numbers)

entonces todo hubiera estado bien.

Se podría decir que estaba siendo tonto por usar el mismo nombre de variable en el mismo ámbito.

Yo no estaba El código estaba bien originalmente: las dos x no estaban en el mismo ámbito.
Fue solo después de mover el bloque interior a una sección diferente del código que surgió el problema (lea: problema durante el mantenimiento, no desarrollo), y no lo esperaba.

Sí, si nunca comete este error , las listas de comprensión son más elegantes.
Pero a partir de la experiencia personal (y de ver a otros cometer el mismo error), he visto que esto sucede lo suficiente como para que no valga la pena el dolor que tiene que atravesar cuando estos errores ingresan en su código.

Conclusión:

Usa el map y el filter . Previenen errores sutiles difíciles de diagnosticar relacionados con el alcance.

Nota al margen:

¡No olvide considerar usar imap y ifilter (en itertools ) si son adecuados para su situación!

¿Hay alguna razón para preferir usar map() sobre la comprensión de lista o viceversa? ¿Alguno de ellos es generalmente más eficiente o se considera generalmente más pitón que el otro?


Aquí hay un caso posible:

map(lambda op1,op2: op1*op2, list1, list2)

versus:

[op1*op2 for op1,op2 in zip(list1,list2)]

Supongo que el zip () es una carga desafortunada e innecesaria que debe realizar si insiste en usar las listas de comprensión en lugar del mapa. Sería genial si alguien aclara esto de manera afirmativa o negativa.


Considero que la forma más Pythonic es usar una lista de comprensión en lugar de un map y filter . La razón es que las listas de comprensión son más claras que el map y el filter .

In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter In [3]: odd_cubes == odd_cubes_alt Out[3]: True

Como usted ve, una comprensión no requiere expresiones lambda adicionales como las necesidades del map . Además, una comprensión también permite filtrar fácilmente, mientras que el map requiere un filter para permitir el filtrado.


Considero que las comprensiones de listas son generalmente más expresivas de lo que estoy tratando de hacer que de map , ambas lo logran, pero la primera guarda la carga mental de tratar de entender lo que podría ser una expresión lambda compleja.

También hay una entrevista en algún lugar (no puedo encontrarlo de antemano) donde Guido enumera lambda s y las funciones funcionales como la cosa que más lamenta de aceptar en Python, por lo que podría argumentar que no son Pythonic. virtud de eso


En realidad, las interpretaciones de map y listas se comportan de manera muy diferente en el lenguaje Python 3. Echa un vistazo al siguiente programa de Python 3:

def square(x): return x*x squares = map(square, [1, 2, 3]) print(list(squares)) print(list(squares))

Puede esperar que imprima la línea "[1, 4, 9]" dos veces, pero en su lugar imprime "[1, 4, 9]" seguido de "[]". La primera vez que miras los squares parece que se comporta como una secuencia de tres elementos, pero la segunda vez como un vacío.

En el map idiomas de Python 2, se muestra una lista antigua, tal como lo hacen las comprensiones de listas en ambos idiomas. El problema es que el valor de retorno del map en Python 3 (e imap en Python 2) no es una lista, ¡es un iterador!

Los elementos se consumen cuando se itera en un iterador, a diferencia de cuando se itera en una lista. Esta es la razón por la que los squares aparecen vacíos en la última línea de print(list(squares)) .

Para resumir:

  • Cuando se trata de iteradores, debe recordar que son de estado y que mutan a medida que los atraviesa.
  • Las listas son más predecibles, ya que solo cambian cuando las mates explícitamente; son contenedores .
  • Y una ventaja adicional: los números, las cadenas y las tuplas son aún más predecibles ya que no pueden cambiar en absoluto; son valores

Entonces, ya que Python 3, map() es un iterador, debe tener en cuenta lo que necesita: un iterador o un objeto de list .

Como ya se mentioned @AlexMartelli, map() es más rápido que la comprensión de la lista solo si no usa la función lambda .

Les presentaré algunas comparaciones de tiempo.

Python 3.5.2 y CPython
He usado el cuaderno de Júpiter y especialmente el comando mágico incorporado %timeit
Medidas : s == 1000 ms == 1000 * 1000 µs = 1000 * 1000 * 1000 ns

Preparar:

x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)] i_list = list(range(1000))

Función incorporada:

%timeit map(sum, x_list) # creating iterator object # Output: The slowest run took 9.91 times longer than the fastest. # This could mean that an intermediate result is being cached. # 1000000 loops, best of 3: 277 ns per loop %timeit list(map(sum, x_list)) # creating list with map # Output: 1000 loops, best of 3: 214 µs per loop %timeit [sum(x) for x in x_list] # creating list with list comprehension # Output: 1000 loops, best of 3: 290 µs per loop

función lambda

%timeit map(lambda i: i+1, i_list) # Output: The slowest run took 8.64 times longer than the fastest. # This could mean that an intermediate result is being cached. # 1000000 loops, best of 3: 325 ns per loop %timeit list(map(lambda i: i+1, i_list)) # Output: 1000 loops, best of 3: 183 µs per loop %timeit [i+1 for i in i_list] # Output: 10000 loops, best of 3: 84.2 µs per loop

También existe una expresión generadora, ver PEP-0289 . Así que pensé que sería útil agregarlo a la comparación.

%timeit (sum(i) for i in x_list) # Output: The slowest run took 6.66 times longer than the fastest. # This could mean that an intermediate result is being cached. # 1000000 loops, best of 3: 495 ns per loop %timeit list((sum(x) for x in x_list)) # Output: 1000 loops, best of 3: 319 µs per loop %timeit (i+1 for i in i_list) # Output: The slowest run took 6.83 times longer than the fastest. # This could mean that an intermediate result is being cached. # 1000000 loops, best of 3: 506 ns per loop %timeit list((i+1 for i in i_list)) # Output: 10000 loops, best of 3: 125 µs per loop

Necesitas objeto de list :

Use la comprensión de lista si es una función personalizada, use list(map()) si hay una función incorporada

No necesita un objeto de list , solo necesita uno iterable:

Siempre usa map() !


Otra razón para usar la comprensión de listas sobre map () y filter () es que Psyco no puede compilar estas funciones.

Ver http://psyco.sourceforge.net/


Si planea escribir cualquier código asíncrono, paralelo o distribuido, es probable que prefiera map sobre una comprensión de lista, ya que la mayoría de los paquetes asíncronos, paralelos o distribuidos proporcionan una función de map para sobrecargar el map de Python. Luego, al pasar la función de map apropiada al resto de su código, es posible que no tenga que modificar su código de serie original para que se ejecute en paralelo (etc.).


map puede ser microscópicamente más rápido en algunos casos (cuando NO está creando un lambda para este propósito, sino utilizando la misma función en el mapa y una lista de componentes). La lista de comprensión puede ser más rápida en otros casos y la mayoría (no todos) de los pitones los consideran más directos y claros.

Un ejemplo de la pequeña ventaja de velocidad del mapa cuando se usa exactamente la misma función:

$ python -mtimeit -s''xs=range(10)'' ''map(hex, xs)'' 100000 loops, best of 3: 4.86 usec per loop $ python -mtimeit -s''xs=range(10)'' ''[hex(x) for x in xs]'' 100000 loops, best of 3: 5.58 usec per loop

Un ejemplo de cómo la comparación de rendimiento se invierte completamente cuando el mapa necesita un lambda:

$ python -mtimeit -s''xs=range(10)'' ''map(lambda x: x+2, xs)'' 100000 loops, best of 3: 4.24 usec per loop $ python -mtimeit -s''xs=range(10)'' ''[x+2 for x in xs]'' 100000 loops, best of 3: 2.32 usec per loop


Casos

  • Caso común : Casi siempre, querrá usar una lista de comprensión en python porque será más obvio lo que está haciendo a los programadores principiantes que leen su código. (Esto no se aplica a otros idiomas, donde pueden aplicarse otros modismos). Incluso será más obvio lo que está haciendo con los programadores de Python, ya que las comprensiones de listas son el estándar de facto en python para la iteración; ellos son esperados
  • Caso menos común : Sin embargo, si ya tiene una función definida , a menudo es razonable usar el map , aunque se considera "no-fitónico". Por ejemplo, map(sum, myLists) es más elegante / terso que [sum(x) for x in myLists] . Obtiene la elegancia de no tener que crear una variable ficticia (por ejemplo, sum(x) for x... o sum(_) for _... o sum(readableName) for readableName... ) que debe escribir Dos veces, sólo para iterar. El mismo argumento es válido para filter y reduce y cualquier cosa del módulo itertools : si ya tiene una función a mano, puede seguir adelante y hacer algo de programación funcional. Esto gana legibilidad en algunas situaciones, y lo pierde en otras (por ejemplo, programadores novatos, múltiples argumentos) ... pero la legibilidad de su código depende en gran medida de sus comentarios.
  • Casi nunca : es posible que desee utilizar la función de map como una función pura y abstracta mientras realiza la programación funcional, donde está mapeando el map , o alternando el map , o de lo contrario se beneficia de hablar del map como una función. En Haskell, por ejemplo, una interfaz de functor llamada fmap generaliza el mapeo sobre cualquier estructura de datos. Esto es muy poco común en Python porque la gramática de Python le obliga a usar el estilo de generador para hablar de iteración; No puedes generalizarlo fácilmente. (Esto a veces es bueno y a veces malo). Probablemente puedas encontrar ejemplos raros de python en los que el map(f, *lists) sea ​​algo razonable. El ejemplo más cercano que puedo encontrar sería sumEach = partial(map,sum) , que es una sumEach = partial(map,sum) de línea que es aproximadamente equivalente a:

def sumEach(myLists): return [sum(_) for _ in myLists]

  • Solo usando un for loop : Por supuesto, también puede usar un for-loop. Aunque no son tan elegantes desde el punto de vista de la programación funcional, a veces las variables no locales hacen que el código sea más claro en lenguajes de programación imperativos como python, porque las personas están muy acostumbradas a leer código de esa manera. Los bucles for también son, por lo general, los más eficientes cuando simplemente está realizando una operación compleja que no está construyendo una lista, como la lista de comprensión y el mapa están optimizados para (por ejemplo, sumar o hacer un árbol, etc.), al menos eficiente en términos de memoria (no necesariamente en términos de tiempo, donde esperaría, en el peor de los casos, un factor constante, a menos que exista algún hipo patológico raro de recolección de basura).

"Pitonismo"

No me gusta la palabra "pythonic" porque no encuentro que pythonic sea siempre elegante en mis ojos. Sin embargo, el map y el filter y otras funciones similares (como el módulo de itertools muy útil) probablemente se consideran antípticos en términos de estilo.

pereza

En términos de eficiencia, como la mayoría de las construcciones de programación funcionales, MAP PUEDE SER PERZOSO , y de hecho es perezoso en Python. Eso significa que puedes hacer esto (en python3 ) y tu computadora no se quedará sin memoria y perderá todos tus datos no guardados:

>>> map(str, range(10**100)) <map object at 0x2201d50>

Intenta hacer eso con una lista de comprensión:

>>> [str(n) for n in range(10**100)] # DO NOT TRY THIS AT HOME OR YOU WILL BE SAD #

Tenga en cuenta que las comprensiones de listas también son intrínsecamente perezosas, pero python ha elegido implementarlas como no perezosas . Sin embargo, python admite la comprensión de listas perezosas en forma de expresiones generadoras, de la siguiente manera:

>>> (str(n) for n in range(10**100)) <generator object <genexpr> at 0xacbdef>

Básicamente, puede pensar que la [...] sintaxis pasa una expresión generadora al constructor de listas, como la list(x for x in range(5)) .

Breve ejemplo artificial

from operator import neg print({x:x**2 for x in map(neg,range(5))}) print({x:x**2 for x in [-y for y in range(5)]}) print({x:x**2 for x in (-y for y in range(5))})

Las comprensiones de la lista no son perezosas, por lo que pueden requerir más memoria (a menos que use las comprensiones del generador). Los corchetes a menudo [...] hacen que las cosas sean obvias, especialmente cuando están entre paréntesis. Por otro lado, a veces terminas siendo verboso como escribir [x for x in... Mientras mantengas cortas las variables de tu iterador, la comprensión de las listas suele ser más clara si no se sangra el código. Pero siempre puedes sangrar tu código.

print( {x:x**2 for x in (-y for y in range(5))} )

o romper las cosas:

rangeNeg5 = (-y for y in range(5)) print( {x:x**2 for x in rangeNeg5} )

Comparación de eficiencia para python3

map es ahora perezoso:

% python3 -mtimeit -s ''xs=range(1000)'' ''f=lambda x:x'' ''z=map(f,xs)'' 1000000 loops, best of 3: 0.336 usec per loop ^^^^^^^^^

Por lo tanto, si no va a utilizar todos sus datos, o si no sabe de antemano la cantidad de datos que necesita, mapee en python3 (y las expresiones generadoras en python2 o python3) evitará calcular sus valores hasta el último momento necesario. Por lo general, esto usualmente será mayor que cualquier sobrecarga por el uso del map . La desventaja es que esto es muy limitado en python en comparación con la mayoría de los lenguajes funcionales: solo obtiene este beneficio si accede a sus datos de izquierda a derecha "en orden", porque las expresiones del generador de python solo se pueden evaluar en el orden x[0], x[1], x[2], ...

Sin embargo, digamos que tenemos una función pre-hecha f nos gustaría map , e ignoramos la pereza del map forzando inmediatamente la evaluación con la list(...) . Conseguimos algunos resultados muy interesantes:

% python3 -mtimeit -s ''xs=range(1000)'' ''f=lambda x:x'' ''z=list(map(f,xs))'' 10000 loops, best of 3: 165/124/135 usec per loop ^^^^^^^^^^^^^^^ for list(<map object>) % python3 -mtimeit -s ''xs=range(1000)'' ''f=lambda x:x'' ''z=[f(x) for x in xs]'' 10000 loops, best of 3: 181/118/123 usec per loop ^^^^^^^^^^^^^^^^^^ for list(<generator>), probably optimized % python3 -mtimeit -s ''xs=range(1000)'' ''f=lambda x:x'' ''z=list(f(x) for x in xs)'' 1000 loops, best of 3: 215/150/150 usec per loop ^^^^^^^^^^^^^^^^^^^^^^ for list(<generator>)

Los resultados se encuentran en la forma AAA / BBB / CCC donde A se realizó con una estación de trabajo Intel alrededor de 2010 con python 3, y B y C se realizaron con una estación de trabajo AMD aproximadamente 2013 con python 3.2.1, Con hardware extremadamente diferente. El resultado parece ser que las interpretaciones de mapas y listas son comparables en cuanto a rendimiento, que se ve más fuertemente afectada por otros factores aleatorios. Lo único que podemos decir es que, por extraño que parezca, aunque esperamos que las comprensiones de listas funcionen mejor que las expresiones generadoras (...) , el map TAMBIÉN es más eficiente que las expresiones generadoras (nuevamente, suponiendo que todos los valores son evaluado / utilizado).

Es importante darse cuenta de que estas pruebas asumen una función muy simple (la función de identidad); sin embargo, esto está bien porque si la función fuera complicada, entonces la sobrecarga de rendimiento sería despreciable en comparación con otros factores en el programa. (Puede ser interesante probar con otras cosas simples como f=lambda x:x+x )

Si tiene habilidad para leer el ensamblaje de python, puede usar el módulo dis para ver si eso es realmente lo que está sucediendo detrás de la escena:

>>> listComp = compile(''[f(x) for x in xs]'', ''listComp'', ''eval'') >>> dis.dis(listComp) 1 0 LOAD_CONST 0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>) 3 MAKE_FUNCTION 0 6 LOAD_NAME 0 (xs) 9 GET_ITER 10 CALL_FUNCTION 1 13 RETURN_VALUE >>> listComp.co_consts (<code object <listcomp> at 0x2511a48, file "listComp", line 1>,) >>> dis.dis(listComp.co_consts[0]) 1 0 BUILD_LIST 0 3 LOAD_FAST 0 (.0) >> 6 FOR_ITER 18 (to 27) 9 STORE_FAST 1 (x) 12 LOAD_GLOBAL 0 (f) 15 LOAD_FAST 1 (x) 18 CALL_FUNCTION 1 21 LIST_APPEND 2 24 JUMP_ABSOLUTE 6 >> 27 RETURN_VALUE

>>> listComp2 = compile(''list(f(x) for x in xs)'', ''listComp2'', ''eval'') >>> dis.dis(listComp2) 1 0 LOAD_NAME 0 (list) 3 LOAD_CONST 0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>) 6 MAKE_FUNCTION 0 9 LOAD_NAME 1 (xs) 12 GET_ITER 13 CALL_FUNCTION 1 16 CALL_FUNCTION 1 19 RETURN_VALUE >>> listComp2.co_consts (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,) >>> dis.dis(listComp2.co_consts[0]) 1 0 LOAD_FAST 0 (.0) >> 3 FOR_ITER 17 (to 23) 6 STORE_FAST 1 (x) 9 LOAD_GLOBAL 0 (f) 12 LOAD_FAST 1 (x) 15 CALL_FUNCTION 1 18 YIELD_VALUE 19 POP_TOP 20 JUMP_ABSOLUTE 3 >> 23 LOAD_CONST 0 (None) 26 RETURN_VALUE

>>> evalledMap = compile(''list(map(f,xs))'', ''evalledMap'', ''eval'') >>> dis.dis(evalledMap) 1 0 LOAD_NAME 0 (list) 3 LOAD_NAME 1 (map) 6 LOAD_NAME 2 (f) 9 LOAD_NAME 3 (xs) 12 CALL_FUNCTION 2 15 CALL_FUNCTION 1 18 RETURN_VALUE

Parece que es mejor usar [...] sintaxis que list(...) . Lamentablemente, la clase de map es un poco opaca al desensamblaje, pero podemos hacerla con nuestra prueba de velocidad.