2011-11-08 5 views
6

Esta pregunta es un seguimiento en a question about Python variable scope. Las preguntas adicionales q1, q2 y answers se pueden encontrar en SO, entre otros. El oficial Python documentation y PEP 3104 se supone que explican los detalles, pero no parecen completamente autoexplicativos para mí.Python no local depende del nivel de jerarquía?

El tema que trato de resolver es refactorizar el código que contiene nonlocal/global moviendo ese código arriba/abajo un nivel de jerarquía.

Lo que no entiendo son las implicaciones de esta sentencia de la referencia de Python:

Names listed in a nonlocal statement, unlike to those listed in a global statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot be determined unambiguously).

Dado el siguiente código en el ámbito global:

var = 0 
def outer(): 
    global var     # line A 
    var = 1 
    def inner(): 
     nonlocal var   # line B 
     var = 2 

     print('inner:', var) 

    inner() 
    print('outer:', var) 

outer() 
print('main:', var) 

ejecución genera un error:

SyntaxError: no binding for nonlocal 'var' found 

El código funciona (con diferente semántica, Por supuesto, si cualquiera de las líneas A está comentado:

inner: 2 
outer: 2 
main: 0 

o línea B es comentada:

inner: 2 
outer: 1 
main: 1 

Sin embargo, en el ejemplo anterior, y dado que nonlocal se supone para unirse var a la " encerrando el alcance ", hubiera esperado que la línea A vincule el ámbito externo/var con el ámbito global y la línea B, luego busque outer/var y también vuelva a enlazar inner/var con global/var. En cambio, parece no encontrarlo en absoluto (debido a la reenlace en la línea A, supongo) y generar un error.

El resultado deseado era lo que esperaba:

inner: 2 
outer: 2 
main: 2 

Se trata sólo de un ejemplo más del estado de ánimo confuso de la determinación del alcance en Python?

O, para hacer de esto una cuestión constructiva:

  • ¿Cómo puede un ejemplo de ello puede escribir de una manera que no importa en qué nivel reside una función (tener que intercambiar global con nonlocal y viceversa)?
  • Si las funciones residen en un nivel intermedio y desconocido de jerarquía, ¿cómo podría el autor de outer() cambiar el código que no se debe tocar el nivel más externo (en este caso global) ni el inner()? -

En mi humilde comprensión del lenguaje, construcciones como estas (depende de los cierres) deben ser evitadas. Otros ya han sugerido utilizar otras funciones de idioma (classes, func attrs) para lograr este tipo de sensibilidad de contexto.

+0

Las variables globales y locales son fundamentalmente diferentes en Python. Las sentencias 'nonlocal' solo pueden referirse a variables locales de un alcance envolvente y nunca a variables globales; para eso es' global', después de todo. Los conceptos detrás de 'global' y' nonlocal' son bastante diferentes. Sus argumentos sobre cómo el estado actual debería plantear un problema están completamente construidos. Mi respuesta sería: No use variables globales, y no use 'global', y todos los" problemas "mencionados (y algunos más) desaparecerán. –

+0

Aunque generalmente estoy de acuerdo en evitar el uso de globales, otros parecen encontrar el concepto tan útil que Python proporciona la palabra clave 'global' y Py3k incluso agrega un' nonlocal'. Estoy tratando de entender las implicaciones de su uso combinado. – cfi

+1

"... y Py3k incluso agregó un' no local'. " 'nonlocal' está destinado a hacer que los cierres sean más útiles. No está relacionado con variables globales. –

Respuesta

6

global y nonlocal no están destinados a combinarse.Que significan cosas diferentes:

  • global significa el nombre existe en el nivel de módulo
  • nonlocal significa el nombre existe en un ámbito de la función léxica exterior

La razón de que está recibiendo la excepción original se debe a le dijo a Python que var es no local (lo que significa que está en una definición de función externa), pero no hay ninguna vinculación de nivel de función para var en ninguna definición de función externa porque le dijo a Python la función externa que var era global.

+0

"global y no local no están destinados a combinarse" y ese es exactamente mi problema: si no, la refacturación del código se hace más difícil. Dado que una función más interna utiliza un 'no local 'y la función circundante inmediata también. Si la función circundante cambia el enlace a 'global', entonces también se debe cambiar la función más interna. Pero, por desgracia, con la sintaxis a mano no veo otra manera. (Pero para usar envoltorios de clases y cosas por el estilo) – cfi

+0

Entiendo su punto, pero no me parece una cantidad irrazonable de trabajo si está refaccionando los cierres para que no cierren. –

0

How can such an example be written in a way that it does not matter at which level a function resides (having to exchange global with nonlocal and vice versa)?

No importa a qué nivel resida una función. Solo importa en qué nivel reside la variable.

If the functions reside at an intermediate, and unknown level of hierarchy, how could the author of outer() change the code that neither the outermost (in this case global) level, nor the inner() level have to be touched?

Usted está pidiendo si es posible para una función en un nivel intermedio que, al cambiar algo en su código, para mantener la variable en una función interna para cambiar alternativamente algo en un ámbito global o algo en el alcance local de la función externa. Esto parece una cosa realmente extraña de poder hacer.

+0

Mi punto es acerca de la refactorización: si la función del medio junto con su función interna se mueve un nivel más bajo, un 'no-local' tendría que cambiarse en' global', incluso si su función circundante inmediata también haría uso de la variable. Hm, empiezo a pensar que he elegido un mal ejemplo para detallar el problema que tengo con esta sintaxis/scoping. – cfi

+0

@cfi Solo quiero asegurarme de que entiendo lo que quiere decir con: "Si la función del medio junto con su función interna se mueve un nivel más bajo". Inicialmente, tanto 'var' como' outer' están dentro del bloque de otra función, por lo tanto tanto 'outer' como' inner' usan 'var no local'. Al mover la "función intermedia junto con su función interna un nivel más bajo", ¿quiere decir que se mueven tanto 'var' como' outer' más cerca del alcance global? Y si este nuevo ámbito para 'var' y' outer' fuera el alcance global, tendría que cambiar tanto 'var no local' en' outer' y en 'inner' a' global var'? – user42768

Cuestiones relacionadas