web-dev-qa-db-fra.com

Pourquoi la carte renvoie-t-elle un objet de la carte plutôt qu'une liste dans Python 3?

Je suis intéressé à comprendre le nouvelle conception du langage de Python 3.x .

J'aime bien, dans Python 2.7, la fonction map:

Python 2.7.12
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: [2, 3, 4]

Cependant, dans Python 3.x, les choses ont changé:

Python 3.5.1
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: <map at 0x4218390>

Je comprends le comment, mais je n'ai pas trouvé de référence au pourquoi. Pourquoi les concepteurs de langage ont-ils fait ce choix qui, à mon avis, introduit beaucoup de douleur. Était-ce pour aider les développeurs à s'en tenir à la liste des ententes?

OMI, la liste peut être naturellement pensée comme Functors ; et on m'a en quelque sorte pensé pour penser de cette façon:

fmap :: (a -> b) -> f a -> f b
70
NoIdeaHowToFixThis

Je pense que la raison pour laquelle la carte existe toujours du tout quand expressions du générateur existe aussi, c'est qu'elle peut prendre plusieurs arguments d'itérateur qui sont tous bouclés et passés dans la fonction:

>>> list(map(min, [1,2,3,4], [0,10,0,10]))
[0,2,0,4]

C'est un peu plus facile que d'utiliser Zip:

>>> list(min(x, y) for x, y in Zip([1,2,3,4], [0,10,0,10]))

Sinon, cela n'ajoute rien aux expressions du générateur.

30
RemcoGerlich

Puisqu'il retourne un itérateur, il omet de stocker la liste de taille complète dans la mémoire. De sorte que vous puissiez facilement y parcourir à l'avenir sans faire de mal à la mémoire. Vous n'avez peut-être même pas besoin d'une liste complète, mais de la partie jusqu'à ce que votre état soit atteint.

Vous pouvez trouver cela docs utile, les itérateurs sont géniaux.

Un objet représentant un flux de données. Les appels répétés à la méthode __next__() de l'itérateur (ou transmis à la fonction intégrée next()) renvoient des éléments successifs dans le flux. Lorsqu'il n'y a plus de données disponibles, une exception StopIteration est générée. À ce stade, l'objet itérateur est épuisé et tout appel ultérieur à sa méthode __next__() soulève simplement StopIteration à nouveau. Les itérateurs doivent avoir une méthode __iter__() qui retourne l'objet itérateur lui-même, de sorte que chaque itérateur est également itérable et peut être utilisé dans la plupart des endroits où d'autres itérables sont acceptés. Une exception notable est le code qui tente plusieurs passes. Un objet conteneur (tel qu'un list) crée un nouvel itérateur chaque fois que vous le transmettez à la fonction iter() ou que vous l'utilisez dans une boucle for. Si vous tentez ceci avec un itérateur, vous obtiendrez simplement le même objet itérateur épuisé que celui utilisé lors de la dernière itération, le faisant apparaître comme un conteneur vide.

21
vishes_shell

Guido répond à cette question ici : ", car créer une liste ne serait qu'un gaspillage ".

Il dit également que la transformation correcte consiste à utiliser une boucle for régulière.

Convertir map() de 2 à 3 n'est peut-être pas simplement un simple cas de collage d'une list( ). Guido dit aussi:

"Si les séquences d'entrée n'ont pas la même longueur, map() s'arrêtera à la fin de la plus courte des séquences. Pour une compatibilité totale avec map() de Python 2.x, également envelopper les séquences dans itertools.Zip_longest(), par exemple.

map(func, *sequences)

devient

list(map(func, itertools.Zip_longest(*sequences)))

"

13
cdarke

Dans Python 3 de nombreuses fonctions (pas seulement map mais Zip, range et autres) renvoient un itérateur plutôt que la liste complète. Vous voudrez peut-être un itérateur (par exemple, pour éviter de conserver toute la liste en mémoire) ou une liste (par exemple, pour pouvoir indexer).

Cependant, je pense que la principale raison de la modification de Python 3 est la suivante: bien que convertir un itérateur en liste à l'aide de list(some_iterator) l'équivalent inverse iter(some_list) ne produise pas la résultat souhaité car la liste complète a déjà été construite et conservée en mémoire.

Par exemple, dans Python 3 list(range(n)) fonctionne parfaitement, car la construction de l'objet range et sa conversion en liste sont peu onéreuses. Cependant, dans Python 2 iter(range(n)) n'enregistre aucune mémoire car la liste complète est construite par range() avant la construction de l'itérateur.

Par conséquent, dans Python 2, des fonctions distinctes sont nécessaires pour créer un itérateur plutôt qu'une liste, telle que imap pour map (bien qu'ils soient pas tout à fait équivalents ), xrange pour range, izip pour Zip. En revanche, Python 3 ne requiert qu'une seule fonction, car l'appel list() crée la liste complète si nécessaire.

10
Chris_Rands