2011-10-28 32 views
38

Supongamos que tengo el siguiente código Python:Python sobreescritura de variables en funciones anidadas

def outer(): 
    string = "" 
    def inner(): 
     string = "String was changed by a nested function!" 
    inner() 
    return string 

Quiero una llamada al exterior() para volver "! Cadena fue cambiado por una función anidada", pero me da "" . Concluyo que Python cree que la línea string = "string was changed by a nested function!" es una declaración de una nueva variable local a inner(). Mi pregunta es: ¿cómo le digo a Python que debería usar la cadena externa()? No puedo usar la palabra clave global, porque la cadena no es global, simplemente vive en un ámbito externo. Ideas?

+0

Podría estar relacionado: http://stackoverflow.com/q/146359/212218 –

Respuesta

35

En Python 3.x, puede utilizar la palabra clave nonlocal:

def outer(): 
    string = "" 
    def inner(): 
     nonlocal string 
     string = "String was changed by a nested function!" 
    inner() 
    return string 

En Python 2.x, se puede usar una lista con un solo elemento y sobrescribir que solo elemento:

def outer(): 
    string = [""] 
    def inner(): 
     string[0] = "String was changed by a nested function!" 
    inner() 
    return string[0] 
+0

+1 En cuanto a por qué el último funciona: la asignación de elementos (junto con la asignación de atributos) no es asignación de variables - 'x [...] = ...' solo obtiene 'x' del alcance que sería de todos modos, y la llamada es su método' __setitem__' . Una opción igualmente válida, pero inicialmente más detallada, es crear un objeto (de una clase definida por el usuario, ya que 'object()' no permitirá agregar atributos) cuyo único propósito es retener un atributo. – delnan

+2

Puede crear dicho "objeto de espacio de nombres" usando 'x = tipo (" ",(), {})()'. :) –

+0

Entonces, 'nonlocal' significa:" buscar esta variable en el ámbito externo "- si la variable no se encontraba en el ámbito externo(), ¿Python seguiría buscando encerrar ámbitos? ¿Python eventualmente trataría de encontrar la variable en el alcance global? – Ord

2

para añadir a Sven's answer:

En Python 2.x, sólo se puede leer variables de ámbito exteriores de la posada er alcance La asignación solo creará una nueva variable local (es decir, el alcance interno) que ocultará el ámbito externo uno.

Si desea leer y modificar, se puede utilizar un dict para mantener sus variables en el ámbito exterior, y luego acceder a ellos a través de la dict en el ámbito interno, también mantener su código bastante limpia y legible en presencia de múltiple alcance exterior vars:

def outer(): 
    # hold some text, plus the number of spaces in the text 
    vars = {'text': 'Some text.', 'num_spaces': 1} 
    def inner(): 
     # add some more text 
     more_text = ' Then some more text.' 
     vars['text'] += more_text 
     # keep track of the number of spaces 
     vars['num_spaces'] += more_text.count(' ') 
    inner() 
    return vars['text'], vars['num_spaces'] 

salida:

>>> outer() 
('Some text. Then some more text.', 5) 
19

También puede evitar esto mediante el uso de la función de los atributos:

def outer(): 
    def inner(): 
     inner.string = "String was changed by a nested function!" 
    inner.string = "" 
    inner() 
    return inner.string 

Aclaración: esto funciona tanto en Python 2.x y 3.x

+3

Esto es en realidad una excelente idea. – Alice

3

Esto me pasa con demasiada frecuencia cuando estaba escribiendo una función y de repente me doy cuenta de que podría ser una buena idea tener una función de ayuda más pequeña, pero no realmente útil en ningún otro lado. que naturalmente me hace querer definirlo dentro como una función anidada.

pero tenía experiencia con el objeto anónimo de JAVA (es decir, definir un ejecutable), y la regla era que el objeto anónimo hace una copia de su entorno externo, en este caso las variables del ámbito exterior. Por lo tanto, si la variable externa es inmutable (int, char), no pueden ser modificados por un objeto anónimo ya que se copian por valor mientras que si es un mutable (collection, objects), se pueden cambiar ... ya que se copian con "puntero" (su dirección en la memoria)

si sabe acerca de la programación, considérelo como pasar por valor y pasar por referencia.

en python, es casi lo mismo.x=123 es una asignación, dan la variable x un nuevo significado (no modificar la edad x), list[i]/dict[key] son las operaciones de acceso a objetos, que realmente modifican cosas

concluir, se necesita un objeto mutable ... con el fin de modificar (aunque se puede acceder a una tupla usando [], no se puede usar aquí ya que no es mutable)

Cuestiones relacionadas