Pourquoi les tranches de Python 3 sont-elles toujours des copies et non des vues?
Comme je l'ai seulement remarqué après avoir commenté cette réponse , les tranches en Python 3 renvoient des copies superficielles de ce qu'elles tranchent plutôt que des vues. Pourquoi est-ce toujours le cas? Même en laissant de côté l'utilisation de numpy des vues plutôt que des copies pour le découpage, le fait que dict.keys
, dict.values
, et {[5] } toutes les vues de retour dans Python 3, et qu'il y a beaucoup d'autres aspects de Python 3 orientés vers une plus grande utilisation des itérateurs, il semble qu'il y aurait eu un mouvement vers les tranches devenir similaire. itertools
a une fonction islice
qui fait des tranches itératives, mais qui est plus limitée que le découpage normal et ne fournit pas de fonctionnalité de vue comme dict.keys
ou dict.values
.
En outre, le fait que vous pouvez utiliser l'affectation aux tranches pour modifier la liste originale, mais que les tranches sont elles-mêmes des copies et non des vues, est un aspect contradictoire du langage et semble violer plusieurs des principes illustrés dans le Zen de Python .
C'est, le fait que vous pouvez faire
>>> a = [1, 2, 3, 4, 5]
>>> a[::2] = [0, 0, 0]
>>> a
[0, 2, 0, 4, 0]
, Mais pas
>>> a = [1, 2, 3, 4, 5]
>>> a[::2][0] = 0
>>> a
[0, 2, 3, 4, 5]
Ou quelque chose comme
>>> a = [1, 2, 3, 4, 5]
>>> b = a[::2]
>>> b
view(a[::2] -> [1, 3, 5]) # numpy doesn't explicitly state that its slices are views, but it would probably be a good idea to do it in some way for regular Python
>>> b[0] = 0
>>> b
view(a[::2] -> [0, 3, 5])
>>> a
[0, 2, 3, 4, 5]
Semble quelque peu arbitraire/indésirable.
Je suis au courant de http://www.python.org/dev/peps/pep-3099/ et la partie où il est dit " Les tranches et les tranches étendues ne disparaîtront pas (même si les API __getslice__
et __setslice__
peuvent être remplacées) et ne retourneront pas de vues pour les types d'objets standard.", mais la discussion liée ne mentionne pas pourquoi le la décision de trancher avec des points de vue a été prise; en fait, la majorité des commentaires sur cette suggestion spécifique sur les suggestions énumérées dans le poste initial semblait être positive.
Qu'est-ce qui a empêché quelque chose comme ça d'être implémenté dans Python 3.0, qui a été spécifiquement conçu pour ne pas être strictement rétrocompatible avec Python 2.x et aurait donc été le meilleur moment pour mettre en œuvre un tel changement dans la conception, et y a-t-il quelque chose qui peut l'empêcher dans les futures versions de Python?
2 réponses
En outre, le fait que vous pouvez utiliser l'affectation aux tranches pour modifier la liste d'origine, mais les tranches sont elles-mêmes des copies et non des vues.
Hmm.. ce n'est pas tout à fait raison; bien que je puisse voir comment vous pourriez penser cela. Dans d'autres langues, une affectation de tranche, quelque chose comme:
a[b:c] = d
Est équivalent à
tmp = a.operator[](slice(b, c)) # which returns some sort of reference
tmp.operator=(d) # which has a special meaning for the reference type.
Mais en python, la première instruction est réellement convertie en ceci:
a.__setitem__(slice(b, c), d)
C'est-à-dire qu'une affectation d'élément est réellement spécialement reconnu en python pour avoir une signification particulière, séparée de la recherche d'élément et de l'affectation ; ils peuvent être indépendants. Ceci est cohérent avec python dans son ensemble, car python n'a pas de concepts tels que "lvalues" trouvés en C / C++; il N'y a aucun moyen de surcharger l'opérateur d'affectation lui-même; seulement des cas spécifiques lorsque le côté gauche de l'affectation n'est pas un identifiant simple.
Supposons que les listes ont-elles des vues et que vous avez essayé d'utiliser il:
myView = myList[1:10]
yourList = [1, 2, 3, 4]
myView = yourList
Dans les langages autres que python, il peut y avoir un moyen de pousser yourList
dans myList
, mais en python, puisque le nom myView
apparaît comme un identifiant nu, cela ne peut signifier qu'une variable assignemnt; la vue est perdue.
Eh bien, il semble que j'ai trouvé beaucoup de raisonnement derrière la décision views, en commençant par http://mail.python.org/pipermail/python-3000/2006-August/003224.html (il s'agit principalement de trancher des chaînes, mais au moins un e - mail dans le thread mentionne des objets mutables comme des listes), et aussi certaines choses de:
Http://mail.python.org/pipermail/python-3000/2007-February/005739.html
http://mail.python.org/pipermail/python-dev/2008-May/079692.html et les e-mails suivants dans le fil
On dirait que les avantages de passer à ce style pour Python de base seraient largement compensés par la complexité induite et divers cas de bord indésirables. Oh bien.
...Et comme je l'ai alors commencé à se demander sur la possibilité de simplement remplacer le courant les objets slice
sont travaillés avec une forme itérable à la itertools.islice
, tout comme zip
, map
, etc. tous les itérables de retour au lieu de listes en Python 3, j'ai commencé à réaliser tout le comportement inattendu et les problèmes possibles qui pourraient en découler. Ressemble à ce pourrait être une impasse pour l'instant.
Du côté positif, les tableaux de numpy sont assez flexibles, donc dans les situations où ce genre de chose pourrait être nécessaire, il ne serait pas trop difficile d'utiliser des ndarrays unidimensionnels au lieu de listes. Cependant, il semble que ndarrays ne supporte pas l'utilisation du découpage pour insérer des éléments supplémentaires dans les tableaux, comme cela arrive avec les listes Python:
>>> a = [0, 0]
>>> a[:1] = [2, 3]
>>> a
[2, 3, 0]
Je pense que l'équivalent numpy serait plutôt quelque chose comme ceci:
>>> a = np.array([0, 0]) # or a = np.zeros([2]), but that's not important here
>>> a = np.hstack(([2, 3], a[1:]))
>>> a
array([2, 3, 0])
Un cas un peu plus compliqué:
>>> a = [1, 2, 3, 4]
>>> a[1:3] = [0, 0, 0]
>>> a
[1, 0, 0, 0, 4]
Contre
>>> a = np.array([1, 2, 3, 4])
>>> a = np.hstack((a[:1], [0, 0, 0], a[3:]))
>>> a
array([1, 0, 0, 0, 4])
Et, bien sûr, les exemples numpy ci-dessus ne stockent pas le résultat dans le tableau d'origine comme cela se produit avec l'expansion de la liste Python régulière.