UnboundLocalError с областями вложенных функций - PullRequest
27 голосов
/ 09 апреля 2010

У меня есть такой код (упрощенно):

def outer():
    ctr = 0

    def inner():
        ctr += 1

    inner()

Но ctr вызывает ошибку:

Traceback (most recent call last):
  File "foo.py", line 9, in <module>
    outer()
  File "foo.py", line 7, in outer
    inner()
  File "foo.py", line 5, in inner
    ctr += 1
UnboundLocalError: local variable 'ctr' referenced before assignment

Как я могу это исправить? Я думал, что вложенные рамки позволили бы мне сделать это. Я пробовал с «глобальным», но он все еще не работает.

Ответы [ 4 ]

34 голосов
/ 09 апреля 2010

Если вы используете Python 3, вы можете использовать оператор nonlocal, чтобы включить повторное связывание нелокального имени:

def outer():
    ctr = 0

    def inner():
        nonlocal ctr
        ctr += 1

    inner()

Если вы используете Python 2, у которого нет nonlocal, вам нужно выполнить приращение без привязки к голому имени (сохраняя счетчик как элемент или атрибут какого-то голого имени, не как само название). Например:

...
ctr = [0]

def inner():
    ctr[0] += 1
...

и, конечно, используйте ctr[0] везде, где вы используете голый ctr, теперь в другом месте.

6 голосов
/ 05 октября 2018

Объяснение

Всякий раз, когда значение присваивается переменной внутри функции, python считает эту переменную локальной переменной этой функции. Поскольку оператор ctr += 1 включает присваивание ctr, python считает, что ctr является локальным для функции inner. Следовательно, он даже не пытается посмотреть на значение переменной ctr, которая была определена в outer. То, что видит питон, по существу таково:

def inner():
    ctr = ctr + 1

И я думаю, что мы все можем согласиться с тем, что этот код вызовет ошибку, поскольку к ctr обращаются до того, как он был определен.

(См. Также этот вопрос для получения более подробной информации о том, как python определяет область видимости переменной.)

Решение (в python 3)

В Python 3 введен оператор nonlocal , который во многом похож на оператор global, но позволяет нам получать доступ к переменным окружающей функции (а не к глобальным переменным). Просто добавьте nonlocal ctr вверху функции inner, и проблема исчезнет:

def outer():
    ctr = 0

    def inner():
        nonlocal ctr
        ctr += 1

    inner()

Обходной путь (в python 2)

Поскольку оператор nonlocal не существует в python 2, мы должны быть хитрыми. Есть два простых обходных пути:

  • Удаление всех назначений на ctr

    Поскольку python рассматривает ctr только как локальную переменную, потому что есть присвоение этой переменной, проблема исчезнет, ​​если мы удалим все присвоения имени ctr. Но как мы можем изменить значение переменной, не назначая ее? Легко: мы оборачиваем переменную в изменяемый объект, как список. Затем мы можем изменить этот список, даже не назначая значение имени ctr:

    def outer():
        ctr = [0]
    
        def inner():
            ctr[0] += 1
    
        inner()
    
  • Передача ctr в качестве аргумента inner

    def outer():
        ctr = 0
    
        def inner(ctr):
            ctr += 1
            return ctr
    
        ctr = inner(ctr)
    
6 голосов
/ 09 апреля 2010

С http://www.devshed.com/c/a/Python/Nested-Functions-in-Python/1/

Код в теле вложенной функции может доступ (но не повторное связывание) локальный переменные внешней функции, также известный как свободные переменные вложенного функция.

Итак, вам нужно явно передать ctr в inner.

0 голосов
/ 09 апреля 2010

Как насчет объявления ctr за пределами outer (т. Е. В глобальной области видимости) или любого другого класса / функции? Это сделает переменную доступной и доступной для записи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...