2011-07-22 15 views
5

Me gustaría saber detalles acerca de por qué esto no funciona como se esperaba:¿Por qué el operador Pythons + = (más igual) no modifica las variables de las funciones internas?

def outer(): 
    mylist = [] 
    def inner(): 
     mylist += [1] 

    inner() 

outer() 

Sobre todo porque mylist.__iadd__([1]) funciona bien.

+1

¿Existe una razón por la que no se podía usar '.extend() ¿? – Amber

+3

o '.append()', si solo está agregando un valor. –

+0

Esos son específicos de listas, hay soluciones generales para el problema. – agf

Respuesta

10

El problema es que cuando asigna un nombre de variable dentro de una función, Python supone que está intentando crear una nueva variable local que enmascarará una variable con un nombre similar en el ámbito externo. Dado que += tiene que obtener el valor de mylist antes de que pueda modificarlo, se queja, porque la versión local de mylist aún no está definida. MRAB's answer da una explicación clara de la semántica.

Por otro lado, cuando hace mylist.__iadd__([1]), no está asignando un nuevo nombre de variable dentro de la función. Solo está usando un método incorporado para modificar un nombre de variable ya asignado. Siempre que no intente asignar un nuevo valor al mylist, no tendrá problemas. Por la misma razón, la línea mylist[0] = 5 también funcionaría dentro de inner si la definición de mylist en outer fuera mylist = [1].

Nota sin embargo que si intenta asignar un nuevo valor a mylist en cualquier lugar de la función, mylist.__iadd__([1]) será bien omita:

>>> outer() 
>>> def outer(): 
...  mylist = [] 
...  def inner(): 
...   mylist.__iadd__([1]) 
...   mylist = [] 
...  inner() 
... 
>>> outer() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 6, in outer 
    File "<stdin>", line 4, in inner 
UnboundLocalError: local variable 'mylist' referenced before assignment 

Si desea asignar un nuevo valor a una variable de un ámbito contenedor , en 3.0+ puede usar nonlocal, de la misma manera que usaría global para asignar un nuevo valor a una variable en el ámbito global. Así que en lugar de esto:

>>> mylist = [] 
>>> def inner(): 
...  global mylist 
...  mylist += [1] 
... 
>>> inner() 
>>> mylist 
[1] 

Para ello:

+0

Gracias, eso es muy detallado. No sabía que Python intenta 'x = x .__ iadd __ (y)' (gracias @MRAB) y asumió que simplemente haría 'x .__ iadd __ (y)'. También cambié el nombre de la variable;) – nh2

1

Cualquier variable asignada se supone que es de ámbito local.

Para asignar a una variable global que hace:

global var 
var = 5 

No se puede hacer todo lo que está haciendo en Python 2, pero en Python 3 que puede hacer:

nonlocal mylist 
mylist += [1] 

La alternativa de Python 2 para cambiar un artículo o atributo de algo es

def outer(): 
    mylist = [] 
    def inner(mylist = mylist): 
     mylist += [1] 
    inner() 
outer() 

Si desea reemplazar el valor del variables, es necesario:

def outer(): 
    def setlist(newlist): 
     mylist = newlist 
    mylist = [] 
    def inner(): 
     setlist(['new_list']) 
    inner() 
outer() 
1

Esto se debe a myList dentro inner() no se refiere a myList definido en outer(), y es por eso que el operador de igualdad, más no funciona. Hay dos soluciones que vienen a mi mente.

El primero, se pasa myList a interior como un argumento:

def outer(): 
    mylist = [] 
    def inner(someList): 
     somelist += [1] 

    inner(mylist) 

outer() 

La segunda solución es declarar myList fuera de ambas funciones, y luego se declara como global dentro de ambas funciones:

mylist = [] 
def outer(): 
    global mylist 
    mylist = [] 
    def inner(): 
     global mylist 
     mylist += [1] 

    inner() 

outer() 

Aunque recomendaría la primera solución.

+0

Ambos derrotan el punto entero de un cierre. – agf

+0

pero realmente no creo que su problema se deba al cierre. – Sam

5

Si un nombre está vinculado (se asigna una variable) en una función, ese nombre se trata como local a menos que se declare global.

Así en inner, mylist es local.

Cuando se escribe x += y, en tiempo de ejecución Python tratará:

x = x.__iadd__(y) 

Si eso no funciona, entonces trata de Python:

x = x.__add__(y) 
+0

+1, la semántica específica es útil aquí. Esto también muestra que '__iadd__' modifica el objeto al que está enlazado _y_ devuelve el resultado de esa modificación. No verá muchos métodos integrados "públicos" que lo hagan. – senderle

Cuestiones relacionadas