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
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.
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éenext()
) renvoient des éléments successifs dans le flux. Lorsqu'il n'y a plus de données disponibles, une exceptionStopIteration
est générée. À ce stade, l'objet itérateur est épuisé et tout appel ultérieur à sa méthode__next__()
soulève simplementStopIteration
à 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'unlist
) crée un nouvel itérateur chaque fois que vous le transmettez à la fonctioniter()
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.
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)))
"
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.