Переменная изменения вложенной функции во внешней функции не работает

def some_func(a): 
    def access_a():
        print(a)
    access_a()

выводит значение a, Однако, если я хочу изменить a во вложенной функции вот так:

def some_func(a): 
    def change_a():
        a += 1
        print(a)
    change_a()

это поднимает UnboundLocalError исключение.

я знаю a является нелокальной переменной, но почему я могу получить к ней доступ без объявления nonlocal a?

3 ответа

Решение

Области применения Python 101:

  1. имя, связанное в теле функции, считается local если явно не заявлено global (Python 2.x и 3.x) или nonlocal (Только Python 3.x). Это верно, где бы ни происходило назначение в теле функции. Попытка прочитать локальную переменную до ее привязки - это, конечно, ошибка.

  2. если имя прочитано, но не связано в теле функции, оно будет найдено во входящих областях (внешняя функция (и), если они есть, глобальная область действия). NB: аргументы функций де-факто являются локальными именами, поэтому их никогда не будут искать во вложенных областях.

Обратите внимание, что a += 1 в основном ярлык для a = a + 1так в вашем примере a является локальным (связан в теле функции и явно не объявлен глобальным или нелокальным), но вы пытаетесь прочитать его a = a+1), прежде чем он связан.

В Python 3 вы можете решить это с помощью nonlocal заявление:

>>> def outer(a):
...    def change():
...       nonlocal a
...       a += 1
...    print("before : {}".format(a))
...    change()
...    print ("after : {}".format(a))
... 
>>> outer(42)
before : 42
after : 43

Python 2 не имеет nonlocal так что канонический взлом заключается в том, чтобы обернуть переменную в изменяемый контейнер (как правило, list но подойдет любой изменяемый объект)

>>> def outer(a):
...     _a = [a]
...     def change():
...         _a[0] += 1
...     print("before : {}".format(_a[0]))
...     change()
...     print ("after : {}".format(_a[0]))
... 
>>> outer(42)
before : 42
after : 43

что довольно уродливо, если не сказать больше.

Хотя замыкания довольно удобны, они в основном являются функциональным аналогом объектов: способ разделения состояния между набором функций при сохранении инкапсуляции этого состояния, поэтому, если вы обнаружите, что вам нужна nonlocal переменная, возможно, правильный класс может быть более чистым решением (хотя, возможно, не для вашего примера, который не возвращает внутреннюю функцию, а использует ее только внутри).

У меня есть два решения для вас:

#first one:
# try with list, compound data types dict/list
def some_func(a): 
    def change_a():
        a[0] += 1
        print(a[0])
    change_a()
some_func([1])
>>> 2


#second one
#reference pointer 
from ctypes import *
def some_func_ctypes(a):
    def change_a():
      a[0] += 1
      print a.contents, a[0]
    change_a()

i = c_int(1)
pi = pointer(i)
some_func_ctypes(pi)

>>> c_int(2) 2

Когда вы используете += оператор, есть присвоение нового значения a, Это превращается в местный в глазах переводчика.

Другие вопросы по тегам