Объяснение
Всякий раз, когда значение присваивается переменной внутри функции, 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)