Estoy tratando de implementar un cierre en Python 2.6 y necesito acceder a una variable no local, pero parece que esta palabra clave no está disponible en Python 2.x. ¿Cómo se debe acceder a las variables no locales en los cierres en estas versiones de Python?
117
def inner(): print d; d = {'y': 1}
. Aquí,print d
lee el exteriord
creando así una variable no locald
en el ámbito interior.X = 1
simplemente vincula el nombreX
con un objeto en particular (yint
con el valor1
).X = 1; Y = X
une dos nombres al mismo objeto exacto. De todos modos, algunos objetos son mutables y puedes cambiar sus valores.La siguiente solución está inspirada en la respuesta de Elias Zamaria , pero contrariamente a esa respuesta, maneja múltiples llamadas de la función externa correctamente. La "variable"
inner.y
es local a la llamada actual deouter
. Solo que no es una variable, ya que está prohibido, sino un atributo de objeto (el objeto es la función eninner
sí). Esto es muy feo (tenga en cuenta que el atributo solo se puede crear después deinner
que se define la función) pero parece efectivo.fuente
inc()
ydec()
regresaron de exterior que incrementar y disminuir un contador compartido. Luego, debe decidir a qué función adjuntar el valor actual del contador y hacer referencia a esa función desde las otras. Lo que parece algo extraño y asimétrico. Por ejemplo, endec()
una línea comoinc.value -= 1
.En lugar de un diccionario, hay menos desorden en una clase no local . Modificando el ejemplo de @ ChrisB :
Luego
Cada llamada externa () crea una clase nueva y distinta llamada contexto (no simplemente una nueva instancia). Por lo tanto, evita que @ Nathaniel tenga cuidado con el contexto compartido.
fuente
__slots__ = ()
y crear un objeto en lugar de usar la clase, por ejemplo,context.z = 3
generaría unAttributeError
. Es posible para todas las clases, a menos que hereden de una clase que no define espacios.Creo que la clave aquí es lo que quieres decir con "acceso". No debería haber ningún problema con la lectura de una variable fuera del alcance del cierre, por ejemplo,
debería funcionar como se esperaba (impresión 3). Sin embargo, anular el valor de x no funciona, por ejemplo,
todavía imprimirá 3. Según mi entendimiento de PEP-3104, esto es lo que la palabra clave nonlocal debe cubrir. Como se menciona en el PEP, puede usar una clase para lograr lo mismo (un poco complicado):
fuente
def ns(): pass
seguida dens.x = 3
. No es bonito, pero es un poco menos feo para mí.class Namespace: x = 3
?ns
es un objeto global, por lo que puede hacer referencians.x
a nivel de módulo en laprint
declaración al final .Hay otra forma de implementar variables no locales en Python 2, en caso de que alguna de las respuestas aquí no sea deseable por cualquier motivo:
Es redundante usar el nombre de la función en la declaración de asignación de la variable, pero me parece más simple y limpio que poner la variable en un diccionario. El valor se recuerda de una llamada a otra, al igual que en la respuesta de Chris B.
fuente
f = outer()
y luego lo haceg = outer()
,f
el contador de 'se restablecerá. Esto se debe a que ambos comparten una solaouter.y
variable, en lugar de que cada uno tenga su propia variable independiente. Aunque este código parece más agradable desde el punto de vista estético que la respuesta de Chris B, su forma parece ser la única forma de emular el alcance léxico si desea llamarouter
más de una vez.outer.y
no implica nada local a la llamada de función (instancia)outer()
, pero asigna a un atributo del objeto de función que está vinculado al nombreouter
en su ámbito adjunto . Y, por tanto, se podría haber utilizado igualmente, por escritoouter.y
, cualquier otro nombre en lugar deouter
, siempre que se sepa que está vinculado a ese ámbito. ¿Es esto correcto?outer.y
usar el nombreinner.y
(ya queinner
está vinculado dentro de la llamadaouter()
, que es exactamente el alcance que queremos), pero poniendo la inicializacióninner.y = 0
después de la definición de interno (ya que el objeto debe existir cuando se crea su atributo), pero, por supuesto, antesreturn inner
?Aquí hay algo inspirado en una sugerencia que Alois Mahdal hizo en un comentario con respecto a otra respuesta :
Actualizar
Después de mirar hacia atrás en esto recientemente, me sorprendió lo parecido a un decorador que era, cuando me di cuenta de que implementarlo como uno lo haría más genérico y útil (aunque podría decirse que hacerlo degrada su legibilidad hasta cierto punto).
Tenga en cuenta que ambas versiones funcionan tanto en Python 2 como en 3.
fuente
Hay una verruga en las reglas de alcance de Python: la asignación hace que una variable sea local al alcance de la función que la encierra inmediatamente. Para una variable global, resolvería esto con la
global
palabra clave.La solución es introducir un objeto que se comparte entre los dos ámbitos, que contiene variables mutables, pero se hace referencia a sí mismo a través de una variable que no está asignada.
Una alternativa es la piratería de algunos ámbitos:
Es posible que pueda descubrir algunos trucos para obtener el nombre del parámetro
outer
y luego pasarlo como varname, pero sin depender del nombreouter
que le gustaría, debe usar un combinador Y.fuente
nonlocal
.locals()
crea un diccionario deouter()
s de los locales en el momentoinner()
se define pero cambiando ese diccionario qué no cambiar elv
enouter()
. Esto ya no funcionará cuando tenga más funciones internas que quieran compartir una variable cerrada. Decir unainc()
ydec()
ese incremento y disminuir un contador compartido.nonlocal
es una característica de Python 3.nonlocal
en Python 2 en general . Tus ideas no cubren el caso general, sino solo el que tiene una función interna. Eche un vistazo a esta esencia para ver un ejemplo. Ambas funciones internas tienen su propio contenedor. Necesita un objeto mutable en el alcance de la función externa, como ya sugirieron otras respuestas.nonlocal
palabra clave introducida en Python 3.Otra forma de hacerlo (aunque es demasiado detallado):
fuente
Al extender la elegante solución de Martineau anterior a un caso de uso práctico y algo menos elegante, obtengo:
fuente
Usa una variable global
Personalmente, no me gustan las variables globales. Pero, mi propuesta se basa en la respuesta https://stackoverflow.com/a/19877437/1083704
donde el usuario necesita declarar una variable global
ranks
, cada vez que necesite llamar areport
. Mi mejora elimina la necesidad de inicializar las variables de función del usuario.fuente
inner
, pero no puede asignarla, pero puede modificar sus claves y valores. Esto evita el uso de variables globales.