Las comprensiones están teniendo algunas interacciones inesperadas con el alcance. ¿Es este el comportamiento esperado?
Tengo un método:
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
A riesgo de lloriquear, esta es una brutal fuente de errores. Mientras escribo un código nuevo, de vez en cuando encuentro errores muy extraños debido al reenlace, incluso ahora que sé que es un problema. Necesito hacer una regla como "siempre prefacio vars temporales en listas por comprensión con guión bajo", pero incluso eso no es infalible.
El hecho de que haya esta bomba de tiempo aleatoria en espera niega toda la agradable "facilidad de uso" de las listas por comprensión.
python
binding
list-comprehension
Jabavu Adams
fuente
fuente
for
constructo -loop yfor
-loops variables de fugas . Así que no fue explícito, sino implícitamente declarado.Respuestas:
Las listas de comprensión filtran la variable de control de bucle en Python 2 pero no en Python 3. Aquí está Guido van Rossum (creador de Python) explicando la historia detrás de esto:
fuente
break
, pero irrelevante para las comprensiones. Recuerdo algunas discusiones de comp.lang.python donde la gente quería asignar variables en medio de la expresión. La forma menos loca encontrada fue de valor único para las cláusulas, por ejemplo.sum100 = [s for s in [0] for i in range(1, 101) for s in [s + i]][-1]
, pero solo necesita una var de comprensión local y funciona igual de bien en Python 3. Creo que "filtrar" era la única forma de establecer una variable visible fuera de una expresión. Todos estuvieron de acuerdo en que estas técnicas son horribles :-)Sí, las listas por comprensión "filtran" su variable en Python 2.x, al igual que los bucles for.
En retrospectiva, se reconoció que esto era un error y se evitó con expresiones generadoras. EDITAR: Como señala Matt B. , también se evitó cuando las sintaxis de comprensión de conjuntos y diccionarios se exportaron desde Python 3.
El comportamiento de las listas de comprensión tuvo que dejarse como está en Python 2, pero está completamente arreglado en Python 3.
Esto significa que en todos:
el
x
siempre es local a la expresión, mientras que éstos:en Python 2.x todos filtran la
x
variable al ámbito circundante.ACTUALIZACIÓN para Python 3.8 (?) : ¡ PEP 572 introducirá un
:=
operador de asignación que deliberadamente se escapa de las comprensiones y las expresiones del generador! Está motivado esencialmente por 2 casos de uso: capturar un "testigo" de funciones de terminación anticipada comoany()
yall()
:y actualizando el estado mutable:
Consulte el Apéndice B para conocer el alcance exacto. La variable se asigna en el entorno más cercano
def
olambda
, a menos que esa función lo declarenonlocal
oglobal
.fuente
Sí, la asignación ocurre allí, tal como ocurre en un
for
bucle. No se está creando un nuevo ámbito.Este es definitivamente el comportamiento esperado: en cada ciclo, el valor está vinculado al nombre que especifique. Por ejemplo,
Una vez que se reconoce, parece bastante fácil de evitar: no use nombres existentes para las variables dentro de las comprensiones.
fuente
Curiosamente, esto no afecta al diccionario ni a la comprensión del conjunto.
Sin embargo, se ha corregido en 3 como se indicó anteriormente.
fuente
alguna solución, para python 2.6, cuando este comportamiento no es deseable
fuente
En python3, mientras está en la comprensión de la lista, la variable no cambia después de su alcance, pero cuando usamos un bucle for simple, la variable se reasigna fuera del alcance.
i = 1 imprimir (i) imprimir ([i en rango (5)]) imprimir (i) El valor de i seguirá siendo 1 solo.
Ahora solo use simplemente for loop el valor de i será reasignado.
fuente