Obtenir une map () pour retourner une liste en Python 3.x

523

J'essaie de mapper une liste en hexadécimal, puis j'utilise la liste ailleurs. En python 2.6, c'était facile:

A: Python 2.6:

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

Cependant, en Python 3.1, ce qui précède renvoie un objet de carte.

B: Python 3.1:

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

Comment récupérer la liste mappée (comme dans A ci-dessus) sur Python 3.x?

Sinon, existe-t-il une meilleure façon de procéder? Mon objet de liste initial contient environ 45 éléments et je souhaite les convertir en hexadécimal.

mozami
la source
2
Il est plus pythonique d'utiliser une compréhension de liste . map()a été presque supprimé de la langue car il n'y a aucune raison de l'utiliser sur une compréhension de liste ou une forboucle.
Boris

Réponses:

771

Faites ceci:

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

Dans Python 3+, de nombreux processus qui itèrent sur les itérables renvoient eux-mêmes les itérateurs. Dans la plupart des cas, cela finit par économiser de la mémoire et devrait accélérer les choses.

Si tout ce que vous allez faire est de parcourir cette liste, il n'est même pas nécessaire de la convertir en liste, car vous pouvez toujours itérer sur l' mapobjet comme ceci:

# Prints "ABCD"
for ch in map(chr,[65,66,67,68]):
    print(ch)
Triptyque
la source
15
Bien sûr, vous pouvez également répéter cela: (chr (x) pour x in [65,66,67,68]). Il n'a même pas besoin de carte.
hughdbrown
2
@hughdbrown L'argument pour utiliser les 3.1 mapserait une évaluation paresseuse lors de l'itération d'une fonction complexe, de grands ensembles de données ou de flux.
Andrew Keeton
18
@Andrew Hugh utilise actuellement une compréhension de générateur qui ferait la même chose. Notez les parenthèses plutôt que les crochets.
Triptyque
5
Une autre solution (plus rapide pour les grandes entrées également) lorsque les valeurs sont connues pour être ASCII / latin-1 consiste à effectuer des conversions en masse au niveau de la couche C: bytes(sequence_of_ints_in_range_0_to_256).decode('latin-1')ce qui straccélère en évitant les appels de fonction Python pour chaque élément en faveur d'une conversion en masse de tous les éléments utilisant uniquement des appels de fonction de niveau C. Vous pouvez envelopper ce qui précède listsi vous avez vraiment besoin d'un listdes caractères individuels, mais comme il strest déjà un itérable de ses propres caractères, la seule raison pour laquelle vous le feriez est si vous avez besoin d'une mutabilité.
ShadowRanger
1
list (map (str, [1,2,3])) donne "Erreur d'argument" pour Python 3.4.3 sur CentOS 7. La compréhension de la liste fonctionne.
Andor
108

Nouveau et soigné dans Python 3.5:

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

Grâce aux généralisations de déballage supplémentaires

MISE À JOUR

Toujours à la recherche de voies plus courtes, j'ai découvert que celle-ci fonctionne également:

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

Le déballage fonctionne également en tuples. Notez la virgule à la fin. Cela en fait un tuple de 1 élément. Autrement dit, c'est équivalent à(*map(chr, [66, 53, 0, 94]),)

Il est plus court d'un seul caractère de la version avec les crochets de liste, mais, à mon avis, mieux vaut écrire, car vous commencez tout de suite avec l'astérisque - la syntaxe d'extension, donc je pense que c'est plus doux pour l'esprit. :)

Israel Unterman
la source
11
@Quelklef list()n'a pas l'air aussi soigné
Arijoon
5
@Quelklef: En outre, l'approche de décompression est trivialement plus rapide grâce au fait qu'il n'est pas nécessaire de rechercher le listconstructeur et d'appeler la machinerie générale d'appel des fonctions. Pour une longue entrée, cela n'aura pas d'importance; pour un court, cela peut faire une grande différence. En utilisant le code ci-dessus avec l'entrée comme un tupleafin qu'il ne soit pas reconstruit à plusieurs reprises, les ipythonrepères microbiens montrent que l' list()approche d'emballage prend environ 20% de plus que le déballage. Attention, en termes absolus, nous parlons de 150 ns, ce qui est trivial, mais vous avez l'idée.
ShadowRanger
Quel était le problème avec l'ancien map? Peut-être avec un nouveau nom ( lmap?) Si la nouvelle valeur par défaut est de retourner un itérateur?
Giorgio
1
*map()donne erreur de syntaxe sur Python 3.6: can't use starred expression here. Vous devez le mettre dans un list:[ *map() ]
ALH
4
@ALH Vous avez manqué la virgule à la fin de la commande. Erreur facile à faire!
LondonRob
103

Pourquoi tu ne fais pas ça:

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

Cela s'appelle une compréhension de liste. Vous pouvez trouver de nombreuses informations sur Google, mais voici le lien vers la documentation Python (2.6) sur les compréhensions de liste . Cependant , vous pourriez être plus intéressé par la documentation de Python 3 .

Mark Rushakoff
la source
4
Hmmmm. Peut-être qu'il doit y avoir une publication générale sur les compréhensions de liste, les générateurs, map (), zip (), et beaucoup d'autres qualités d'itération rapide en python.
hughdbrown
46
Je suppose que parce que c'est plus verbeux, vous devez écrire une variable supplémentaire (deux fois) ... Si l'opération est plus complexe et que vous finissez par écrire un lambda, ou que vous devez également supprimer certains éléments, je pense qu'une compréhension est définitivement meilleure qu'une carte + filtre, mais si vous avez déjà la fonction que vous souhaitez appliquer, la carte est plus succincte.
fortran
1
+1: Plus facile à lire et vous permet d'utiliser des fonctions avec de nombreux paramètres
Le Droid
7
map(chr, [66,53,0,94])est définitivement plus concis que [chr(x) for x in [66,53,0,94]].
Giorgio
bien plus vite que les autres réponses
Evhz
25

La fonction de carte de retour de liste a l'avantage d'économiser la saisie, en particulier pendant les sessions interactives. Vous pouvez définir une lmapfonction (sur l'analogie de python2 imap) qui retourne la liste:

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

Ensuite, appeler à la lmapplace de mapfera l'affaire: lmap(str, x)est plus court de 5 caractères (30% dans ce cas) que list(map(str, x))et est certainement plus court que [str(v) for v in x]. Vous pouvez également créer des fonctions similaires filter.

Il y avait un commentaire à la question initiale:

Je suggérerais de renommer Getting map () pour renvoyer une liste en Python 3. * tel qu'il s'applique à toutes les versions de Python3. Y a-t-il un moyen de faire cela? - meawoppl 24 janvier à 17:58

Il est possible de le faire, mais il est une très mauvaise idée. Juste pour le plaisir, voici comment vous pouvez ( mais ne devriez pas ) le faire:

__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
la source
4

Conversion de mon ancien commentaire pour une meilleure visibilité: Pour une "meilleure façon de faire" sans mapentièrement, si vos entrées sont connues pour être des ordinaux ASCII, il est généralement beaucoup plus rapide de convertir byteset de décoder, a la bytes(list_of_ordinals).decode('ascii'). Cela vous donne une strdes valeurs, mais si vous avez besoin d'une listmutabilité ou similaire, vous pouvez simplement la convertir (et c'est encore plus rapide). Par exemple, dans les ipythonmicrobenchmarks convertissant 45 entrées:

>>> %%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 vous le laissez comme un str, cela prend environ 20% du temps des mapsolutions les plus rapides ; même en revenant à la liste, c'est toujours moins de 40% de la mapsolution la plus rapide . Converti en vrac par l' intermédiaire byteset bytes.decodepuis bulk retransférer listpermet d' économiser beaucoup de travail, mais comme il est indiqué, ne fonctionne que si toutes vos entrées sont ordinaux ASCII (ou ordinaux dans certains un octet par paramètres régionaux de codage des caractères spécifique, par exemple latin-1).

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

map (func, * iterables) -> map object Créez un itérateur qui calcule la fonction en utilisant les arguments de chacun des itérables. S'arrête lorsque l'itérable le plus court est épuisé.

"Faire un itérateur"

signifie qu'il renverra un itérateur.

"qui calcule la fonction en utilisant des arguments de chacun des itérables"

signifie que la fonction next () de l'itérateur prendra une valeur de chaque itérable et passera chacun d'eux à un paramètre positionnel de la fonction.

Vous obtenez donc un itérateur de la fonction map () et le transmettez à la fonction intégrée list () ou utilisez des compréhensions de liste.

Ini
la source
2

En plus des réponses ci-dessus Python 3, nous pouvons simplement créer une valeur listde résultat à partir d'un mapas

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

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

Nous pouvons généraliser par un autre exemple où j'ai été frappé, les opérations sur la carte peuvent également être traitées de la même manière que dans le regexproblème, nous pouvons écrire une fonction pour obtenir listdes éléments à mapper et obtenir un jeu de résultats en même temps. 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
la source
@miradulo Je supposais qu'en Python 2, une liste a été retournée, mais en Python 3, seul le type est retourné et j'ai juste essayé de donner au même format. Si vous pensez que c'est inutile, il y a peut-être des gens comme moi qui peuvent le trouver utile et c'est pourquoi j'ai ajouté.
Harry_pb
2
Lorsqu'il y a déjà une compréhension de liste, une fonction de liste et une réponse de décompression, une boucle for explicite n'ajoute pas beaucoup à mon humble avis.
miradulo
1

Vous pouvez essayer d'obtenir une liste à partir de l'objet de carte en itérant simplement chaque élément de l'objet et en le stockant dans une variable différente.

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

En utilisant la compréhension de liste en python et l'utilitaire de fonction de carte de base, on peut aussi le faire:

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

darshan ks
la source
la liste chi contiendra la valeur ASIC des éléments donnés.
darshan ks