Les compréhensions ont des interactions inattendues avec la portée. Est-ce le comportement attendu?
J'ai une méthode:
def leave_room(self, uid):
u = self.user_by_id(uid)
r = self.rooms[u.rid]
other_uids = [ouid for ouid in r.users_by_id.keys() if ouid != u.uid]
other_us = [self.user_by_id(uid) for uid in other_uids]
r.remove_user(uid) # OOPS! uid has been re-bound by the list comprehension above
# Interestingly, it's rebound to the last uid in the list, so the error only shows
# up when len > 1
Au risque de pleurnicher, c'est une source brutale d'erreurs. Au fur et à mesure que j'écris un nouveau code, je trouve occasionnellement des erreurs très étranges dues à la reliure - même maintenant que je sais que c'est un problème. J'ai besoin de faire une règle comme "toujours préface les variables temporaires dans les compréhensions de liste avec un trait de soulignement", mais même ce n'est pas infaillible.
Le fait qu'il y ait cette sorte d'attente aléatoire de bombe à retardement annule toute la "facilité d'utilisation" de la compréhension de liste.
python
binding
list-comprehension
Jabavu Adams
la source
la source
for
construction -loop etfor
-loops des variables fuite . Ce n'était donc pas explicite, mais c'était implicitement déclaré.Réponses:
Les compréhensions de listes fuient la variable de contrôle de boucle dans Python 2 mais pas dans Python 3. Voici Guido van Rossum (créateur de Python) expliquant l'histoire derrière ceci:
la source
break
- mais sans rapport avec les comprehesions. Je me souviens de certaines discussions comp.lang.python où les gens voulaient attribuer des variables au milieu de l'expression. Le moyen le moins insensé trouvé était une valeur unique pour les clauses, par exemple.sum100 = [s for s in [0] for i in range(1, 101) for s in [s + i]][-1]
, mais a juste besoin d'un var local de compréhension et fonctionne aussi bien en Python 3. Je pense que "la fuite" était le seul moyen de définir une variable visible en dehors d'une expression. Tout le monde a convenu que ces techniques sont horribles :-)Oui, les list comprehensions "fuient" leur variable dans Python 2.x, tout comme pour les boucles for.
Rétrospectivement, cela a été reconnu comme une erreur, et cela a été évité avec des expressions génératrices. EDIT: Comme le note Matt B., cela a également été évité lorsque les syntaxes de définition et de compréhension de dictionnaire étaient rétroportées à partir de Python 3.
Le comportement des compréhensions de listes a dû être laissé tel quel dans Python 2, mais il est entièrement corrigé dans Python 3.
Cela signifie que dans l'ensemble de:
le
x
est toujours local à l'expression tandis que ceux-ci:en Python 2.x, tous la
x
variable fuit vers la portée environnante.MISE À JOUR pour Python 3.8 (?) : PEP 572 introduira un
:=
opérateur d'assignation qui fuit délibérément les compréhensions et les expressions du générateur! Il est motivé par essentiellement 2 cas d'utilisation: capturer un «témoin» à partir de fonctions de terminaison anticipée telles queany()
etall()
:et mise à jour de l'état mutable:
Voir l' annexe B pour la portée exacte. La variable est affectée dans l'entourage le plus proche
def
oulambda
, à moins que cette fonction ne le déclarenonlocal
ouglobal
.la source
Oui, l'affectation se produit là, comme elle le ferait en
for
boucle. Aucune nouvelle étendue n'est en cours de création.C'est certainement le comportement attendu: à chaque cycle, la valeur est liée au nom que vous spécifiez. Par exemple,
Une fois que cela est reconnu, cela semble assez facile à éviter: n'utilisez pas de noms existants pour les variables dans les compréhensions.
la source
Fait intéressant, cela n'affecte pas le dictionnaire ou la compréhension d'ensemble.
Cependant, il a été corrigé dans 3 comme indiqué ci-dessus.
la source
une solution de contournement, pour python 2.6, lorsque ce comportement n'est pas souhaitable
la source
En python3, pendant la compréhension de liste, la variable ne reçoit pas de changement une fois sa portée terminée, mais lorsque nous utilisons une simple boucle for, la variable est réaffectée hors de portée.
i = 1 print (i) print ([i in range (5)]) print (i) La valeur de i restera 1 seulement.
Maintenant, utilisez simplement pour la boucle, la valeur de i sera réaffectée.
la source