Ottenere una mappa () per restituire un elenco in Python 3.x


523

Sto cercando di mappare un elenco in esadecimale e quindi utilizzare l'elenco altrove. In Python 2.6, questo è stato facile:

A: Python 2.6:

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

Tuttavia, in Python 3.1, quanto sopra restituisce un oggetto mappa.

B: Python 3.1:

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

Come recuperare l'elenco mappato (come in A sopra) su Python 3.x?

In alternativa, c'è un modo migliore per farlo? Il mio oggetto elenco iniziale ha circa 45 elementi e id mi piace convertirli in esadecimali.


2
È più pitonico usare una comprensione della lista . map()è stato quasi rimosso dalla lingua perché non c'è motivo di usarlo su una comprensione di elenco o su un forciclo.
Boris,

Risposte:


771

Fai questo:

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

In Python 3+, molti processi che ripetono su iterabili restituiscono gli stessi iteratori. Nella maggior parte dei casi, questo finisce per risparmiare memoria e dovrebbe rendere le cose più veloci.

Se alla fine tutto ciò che farai è iterare su questo elenco, non è necessario nemmeno convertirlo in un elenco, perché puoi ancora iterare l' mapoggetto in questo modo:

# Prints "ABCD"
for ch in map(chr,[65,66,67,68]):
    print(ch)

15
Naturalmente, puoi anche iterare su questo: (chr (x) per x in [65,66,67,68]). Non ha nemmeno bisogno di una mappa.
hughdbrown,

2
@hughdbrown L'argomento per usare 3.1 mapsarebbe una valutazione pigra quando si scorre su una funzione complessa, grandi set di dati o flussi.
Andrew Keeton,

18
@Andrew in realtà Hugh sta usando una comprensione del generatore che farebbe la stessa cosa. Nota le parentesi anziché le parentesi quadre.
Trittico

5
Una soluzione alternativa (più veloce anche per input di grandi dimensioni) quando i valori sono noti come ASCII / latin-1 consiste nel fare conversioni di massa a livello C: bytes(sequence_of_ints_in_range_0_to_256).decode('latin-1')ciò rende strpiù veloce evitando chiamate di funzioni Python per ogni elemento a favore di una conversione di massa di tutti gli elementi che usano solo chiamate di funzione di livello C. Puoi includere quanto sopra listse hai davvero bisogno di uno listdei singoli personaggi, ma poiché strè già un iterabile dei suoi stessi personaggi, l'unica ragione per cui lo faresti è se hai bisogno di mutabilità.
ShadowRanger

1
list (map (str, [1,2,3])) fornisce "Errore nell'argomento" per Python 3.4.3 su CentOS 7. La comprensione dell'elenco funziona.
Andor,

108

Nuovo e pulito in Python 3.5:

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

Grazie alle generalizzazioni aggiuntive di disimballaggio

AGGIORNARE

Sempre alla ricerca di modi più brevi, ho scoperto che questo funziona anche:

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

Il disimballaggio funziona anche in tuple. Nota la virgola alla fine. Questo lo rende una tupla di 1 elemento. Cioè, è equivalente a(*map(chr, [66, 53, 0, 94]),)

È più breve di un solo carattere dalla versione con le parentesi di elenco, ma, a mio avviso, è meglio scrivere, perché inizi subito con l'asterisco - la sintassi di espansione, quindi sento che è più morbido nella mente. :)


11
@Quelklef list()non sembra pulito
Arijoon,

5
@Quelklef: Inoltre, l'approccio di spacchettamento è banalmente più veloce grazie alla non necessità di cercare il listcostruttore e invocare i macchinari di chiamata delle funzioni generali. Per un lungo input, non importa; per breve, può fare una grande differenza. Usando il codice sopra con l'input come un tuplemodo per non essere ripetutamente ricostruito, i ipythonmicrobench mark mostrano che l' list()approccio di wrapping richiede circa il 20% in più rispetto al disimballaggio. Intendiamoci, in termini assoluti, stiamo parlando di 150 ns, il che è banale, ma si ottiene l'idea.
ShadowRanger l'

Cosa c'era di sbagliato nel vecchio map? Forse con un nuovo nome ( lmap?) Se il nuovo valore predefinito è di restituire un iteratore?
Giorgio

1
*map()dà errore di sintassi Python 3.6: can't use starred expression here. Devi metterlo in un list:[ *map() ]
ALH il

4
@ALH Hai perso la virgola alla fine del comando. Facile errore da fare!
LondonRob

103

Perché non lo stai facendo:

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

Si chiama comprensione elenco. Puoi trovare molte informazioni su Google, ma ecco il link alla documentazione di Python (2.6) sulla comprensione dell'elenco . Potresti essere più interessato aTuttavia , alla documentazione di Python 3 .


4
Hmmmm. Forse dovrebbe esserci un post generale su comprensioni di elenchi, generatori, map (), zip () e molte altre bontà di iterazione rapida in Python.
hughdbrown,

46
Immagino che sia più prolisso, devi scrivere una variabile extra (due volte) ... Se l'operazione è più complessa e finisci per scrivere un lambda, o devi anche eliminare alcuni elementi, penso che una comprensione sia decisamente migliore di una mappa + filtro, ma se hai già la funzione che desideri applicare, la mappa è più concisa.
fortran,

1
+1: più facile da leggere e consente di utilizzare funzioni con molti parametri
Le Droid

7
map(chr, [66,53,0,94])è decisamente più conciso di [chr(x) for x in [66,53,0,94]].
Giorgio,

molto più veloce delle altre risposte
Evhz

25

La funzione della mappa di ritorno alla lista ha il vantaggio di salvare la digitazione, specialmente durante le sessioni interattive. È possibile definire la lmapfunzione (sull'analogia di python2 imap) che restituisce l'elenco:

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

Quindi chiamare lmapinvece di mapfarà il lavoro: lmap(str, x)è più breve di 5 caratteri (in questo caso il 30%) di list(map(str, x))ed è sicuramente più breve di [str(v) for v in x]. È possibile creare funzioni simili perfilter .

C'è stato un commento alla domanda originale:

Suggerirei di rinominare Getting map () per restituire un elenco in Python 3. * come si applica a tutte le versioni di Python3. C'è un modo per fare questo? - meawoppl 24 gennaio alle 17:58

Si è possibile farlo, ma è una pessima idea. Solo per divertimento, ecco come puoi ( ma non dovresti ) farlo:

__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

4

Convertire il mio vecchio commento per una migliore visibilità: per un "modo migliore per farlo" senza del maptutto, se i tuoi input sono noti per essere ordinali ASCII, è generalmente molto più veloce convertire bytese decodificare, a la bytes(list_of_ordinals).decode('ascii'). Questo ti dà uno strdei valori, ma se hai bisogno di un valore listper mutabilità o simili, puoi semplicemente convertirlo (ed è ancora più veloce). Ad esempio, nei ipythonmicrobenchmark che convertono 45 input:

>>> %%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)

Se lo lasci come a str, ci vuole circa il 20% del tempo delle mapsoluzioni più veloci ; anche convertendo nuovamente in elenco è ancora meno del 40% della mapsoluzione più veloce . La conversione in blocco tramite bytese bytes.decodequindi la conversione in blocco indietro per listrisparmiare molto lavoro, ma come notato, funziona solo se tutti i tuoi input sono ordinali ASCII (o ordinali in un byte specifico per codifica locale per carattere, ad es latin-1.).


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

map (func, * iterables) -> map object Crea un iteratore che calcola la funzione usando gli argomenti di ciascuno degli iterabili. Si interrompe quando il più breve iterabile è esaurito.

"Crea un iteratore"

significa che restituirà un iteratore.

"che calcola la funzione usando gli argomenti di ciascuno degli iterabili"

significa che la funzione next () dell'iteratore prenderà un valore per ogni iterabile e li passerà a un parametro posizionale della funzione.

Quindi ottieni un iteratore dalla mappa () funtion e jsut lo passa alla funzione built-in list () o usa la comprensione dell'elenco.


2

Oltre alle risposte di cui sopra Python 3, possiamo semplicemente creare un valore listdi risultato da un mapas

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

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

Possiamo generalizzare con un altro esempio in cui sono stato colpito, le operazioni sulla mappa possono anche essere gestite in modo simile come in un regexproblema, possiamo scrivere la funzione per ottenere listelementi da mappare e ottenere il set di risultati allo stesso tempo. 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]

@miradulo Suppongo che in Python 2 sia stato restituito un elenco, ma in Python 3 viene restituito solo il tipo e ho appena provato a dare nello stesso formato. Se pensi che sia inutile, forse ci sono persone come me che lo possono trovare utile ed è per questo che l'ho aggiunto.
Harry_pb,

2
Quando esiste già una comprensione dell'elenco, una funzione dell'elenco e una risposta di decompressione, un ciclo esplicito per non aggiunge molto IMHO.
miradulo,

1

Puoi provare a ottenere un elenco dall'oggetto mappa semplicemente ripetendo ogni elemento nell'oggetto e memorizzandolo in una variabile diversa.

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

0

Utilizzando la comprensione dell'elenco in Python e l'utilità di base delle funzioni della mappa, si può fare anche questo:

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


la lista chi conterrà il valore ASIC degli elementi dati.
Darshan,
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.