Obtener un mapa () para devolver una lista en Python 3.x

523

Estoy tratando de mapear una lista en hexadecimal, y luego usar la lista en otro lugar. En python 2.6, esto fue fácil:

A: Python 2.6:

>>> map(chr, [66, 53, 0, 94])
['B', '5', '\x00', '^']

Sin embargo, en Python 3.1, lo anterior devuelve un objeto de mapa.

B: Python 3.1:

>>> map(chr, [66, 53, 0, 94])
<map object at 0x00AF5570>

¿Cómo recupero la lista asignada (como en A arriba) en Python 3.x?

Alternativamente, ¿hay una mejor manera de hacer esto? Mi objeto de lista inicial tiene alrededor de 45 elementos y me gustaría convertirlos en hexadecimal.

mozami
fuente
2
Es más pitónico usar una lista de comprensión . map()fue casi eliminado del lenguaje porque no hay razón para usarlo en una lista de comprensión o un forbucle.
Boris

Respuestas:

771

Hacer esto:

list(map(chr,[66,53,0,94]))

En Python 3+, muchos procesos que iteran sobre iterables devuelven iteradores ellos mismos. En la mayoría de los casos, esto termina ahorrando memoria y debería hacer que las cosas vayan más rápido.

Si todo lo que vas a hacer es iterar sobre esta lista eventualmente, no hay necesidad de convertirla en una lista, porque aún puedes iterar sobre el mapobjeto de esta manera:

# Prints "ABCD"
for ch in map(chr,[65,66,67,68]):
    print(ch)
Tríptico
fuente
15
Por supuesto, también puede iterar sobre esto: (chr (x) para x en [65,66,67,68]). Ni siquiera necesita un mapa.
hughdbrown
2
@hughdbrown El argumento para usar 3.1 mapsería una evaluación perezosa cuando se itera en una función compleja, grandes conjuntos de datos o secuencias.
Andrew Keeton el
18
@Andrew en realidad Hugh está utilizando un generador de comprensión que haría lo mismo. Tenga en cuenta los paréntesis en lugar de corchetes.
Tríptico
55
Una solución alternativa (más rápida para entradas grandes también) cuando se sabe que los valores son ASCII / latin-1 es hacer conversiones masivas en la capa C: lo bytes(sequence_of_ints_in_range_0_to_256).decode('latin-1')que hace strmás rápido al evitar llamadas a la función Python para cada elemento a favor de una conversión masiva de todos los elementos que usan solo llamadas de función de nivel C. Puede incluir lo anterior listsi realmente necesita uno listde los caracteres individuales, pero dado strque ya es un iterable de sus propios caracteres, la única razón por la que lo haría es si necesita mutabilidad.
ShadowRanger 01 de
1
list (map (str, [1,2,3])) da "Error en argumento" para Python 3.4.3 en CentOS 7. La comprensión de listas funciona.
Andor
108

Nuevo y ordenado en Python 3.5:

[*map(chr, [66, 53, 0, 94])]

Gracias a las generalizaciones de desempaquetado adicionales

ACTUALIZAR

Siempre buscando formas más cortas, descubrí que esta también funciona:

*map(chr, [66, 53, 0, 94]),

Desempacar también funciona en tuplas. Tenga en cuenta la coma al final. Esto lo convierte en una tupla de 1 elemento. Es decir, es equivalente a(*map(chr, [66, 53, 0, 94]),)

Es más corto en solo un carácter de la versión con los corchetes de lista, pero, en mi opinión, es mejor escribir, porque comienzas con el asterisco, la sintaxis de expansión, así que siento que es más suave en la mente. :)

Israel Unterman
fuente
11
@Quelklef list()no se ve tan ordenado
Arijoon
55
@Quelklef: Además, el enfoque de desembalaje es trivialmente más rápido gracias a que no es necesario buscar el listconstructor e invocar la maquinaria de llamada de función general. Para una entrada larga, no importará; para uno corto, puede hacer una gran diferencia. Usando el código anterior con la entrada como un elemento tuplepara que no se reconstruya repetidamente, los ipythonmicrobenchmarks muestran que el list()enfoque de envoltura tarda aproximadamente un 20% más que desempaquetar. Eso sí, en términos absolutos, estamos hablando de 150 ns, lo cual es trivial, pero entiendes la idea.
ShadowRanger
¿Qué estaba mal con lo viejo map? ¿Quizás con un nuevo nombre ( lmap?) Si el nuevo valor predeterminado es devolver un iterador?
Giorgio
1
*map()da error de sintaxis en Python 3.6: can't use starred expression here. Debe ponerlo en un list:[ *map() ]
ALH
44
@ALH Te perdiste la coma al final del comando. ¡Error fácil de hacer!
LondonRob
103

¿Por qué no haces esto?

[chr(x) for x in [66,53,0,94]]

Se llama una lista de comprensión. Puede encontrar mucha información en Google, pero aquí está el enlace a la documentación de Python (2.6) sobre comprensiones de listas . Sin embargo, es posible que esté más interesado en la documentación de Python 3 .

Mark Rushakoff
fuente
44
Hmmmm Tal vez deba haber una publicación general sobre comprensiones de listas, generadores, map (), zip () y muchas otras bondades de iteración rápida en python.
hughdbrown
46
Supongo que porque es más detallado, tienes que escribir una variable adicional (dos veces) ... Si la operación es más compleja y terminas escribiendo una lambda, o también necesitas eliminar algunos elementos, creo que una comprensión es definitivamente mejor que un mapa + filtro, pero si ya tiene la función que desea aplicar, el mapa es más sucinto.
fortran
1
+1: más fácil de leer y le permite usar funciones con muchos parámetros
Le Droid
77
map(chr, [66,53,0,94])es definitivamente más concisa que [chr(x) for x in [66,53,0,94]].
Giorgio
mucho más rápido que las otras respuestas
Evhz
25

La función de mapa de retorno de lista tiene la ventaja de guardar la escritura, especialmente durante las sesiones interactivas. Puede definir la lmapfunción (en la analogía de python2 imap) que devuelve la lista:

lmap = lambda func, *iterable: list(map(func, *iterable))

Entonces llamar en lmaplugar de maphará el trabajo: lmap(str, x)es más corto en 5 caracteres (30% en este caso) que list(map(str, x))y ciertamente es más corto que [str(v) for v in x]. También puede crear funciones similares para filter.

Hubo un comentario a la pregunta original:

Sugeriría un cambio de nombre a Getting map () para devolver una lista en Python 3. *, ya que se aplica a todas las versiones de Python3. ¿Hay alguna forma de hacer esto? - meawoppl 24 de enero a las 17:58

Que es posible hacer eso, pero es una idea muy mala. Solo por diversión, así es como puede ( pero no debe ) hacerlo:

__global_map = map #keep reference to the original map
lmap = lambda func, *iterable: list(__global_map(func, *iterable)) # using "map" here will cause infinite recursion
map = lmap
x = [1, 2, 3]
map(str, x) #test
map = __global_map #restore the original map and don't do that again
map(str, x) #iterator
Boris Gorelik
fuente
4

Convertir mi comentario anterior para una mejor visibilidad: para una "mejor manera de hacer esto" sin necesidad de nada map, si se sabe que sus entradas son ordinales ASCII, generalmente es mucho más rápido convertir bytesy decodificar, a la bytes(list_of_ordinals).decode('ascii'). Eso le proporciona uno strde los valores, pero si necesita un valor listde mutabilidad o similar, simplemente puede convertirlo (y aún es más rápido). Por ejemplo, en ipythonmicrobenchmarks que convierten 45 entradas:

>>> %%timeit -r5 ordinals = list(range(45))
... list(map(chr, ordinals))
...
3.91 µs ± 60.2 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)

>>> %%timeit -r5 ordinals = list(range(45))
... [*map(chr, ordinals)]
...
3.84 µs ± 219 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)

>>> %%timeit -r5 ordinals = list(range(45))
... [*bytes(ordinals).decode('ascii')]
...
1.43 µs ± 49.7 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)

>>> %%timeit -r5 ordinals = list(range(45))
... bytes(ordinals).decode('ascii')
...
781 ns ± 15.9 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)

Si lo deja como a str, lleva ~ 20% del tiempo de las mapsoluciones más rápidas ; incluso volver a convertir a la lista sigue siendo menos del 40% de la mapsolución más rápida . La conversión masiva a través de bytesy bytes.decodeluego la conversión masiva de nuevo a listahorra mucho trabajo, pero como se señaló, solo funciona si todas sus entradas son ordinales ASCII (u ordinales en algún byte por codificación específica de localización de caracteres, por ejemplo latin-1).

ShadowRanger
fuente
2
list(map(chr, [66, 53, 0, 94]))

map (func, * iterables) -> map object Crea un iterador que calcule la función usando argumentos de cada uno de los iterables. Se detiene cuando se agota el iterable más corto.

"Hacer un iterador"

significa que devolverá un iterador.

"que calcula la función utilizando argumentos de cada uno de los iterables"

significa que la función next () del iterador tomará un valor de cada iterable y pasará cada uno de ellos a un parámetro posicional de la función.

Entonces obtienes un iterador de la función map () y lo pasas a la función incorporada list () o usas comprensiones de listas.

Ini
fuente
2

Además de las respuestas anteriores en Python 3, simplemente podemos crear un listde valores de los resultados de una mapcomo

li = []
for x in map(chr,[66,53,0,94]):
    li.append(x)

print (li)
>>>['B', '5', '\x00', '^']

Podemos generalizar con otro ejemplo en el que me llamó la atención, las operaciones en el mapa también se pueden manejar de manera similar, como en el regexproblema, podemos escribir la función para obtener listelementos para asignar y obtener el conjunto de resultados al mismo tiempo. Ex.

b = 'Strings: 1,072, Another String: 474 '
li = []
for x in map(int,map(int, re.findall('\d+', b))):
    li.append(x)

print (li)
>>>[1, 72, 474]
Harry_pb
fuente
@miradulo Supuse que en Python 2, se devolvió una lista, pero en Python 3, solo se devuelve el tipo y solo intenté dar en el mismo formato. Si crees que es innecesario, tal vez haya personas como yo que puedan encontrarlo útil y es por eso que agregué.
Harry_pb
2
Cuando ya existe una comprensión de la lista, una función de lista y una respuesta de desempaquetado, un bucle for explícito no agrega mucho en mi humilde opinión.
miradulo
1

Puede intentar obtener una lista del objeto de mapa simplemente iterando cada elemento en el objeto y almacénelo en una variable diferente.

a = map(chr, [66, 53, 0, 94])
b = [item for item in a]
print(b)
>>>['B', '5', '\x00', '^']
Akshay Satpaise
fuente
0

Usando la comprensión de la lista en Python y la utilidad básica de la función de mapa, también se puede hacer esto:

chi = [x for x in map(chr,[66,53,0,94])]

darshan ks
fuente
La lista chi contendrá el valor ASIC de los elementos dados.
darshan ks