Назначение переменной из родительской функции: "Локальная переменная, указанная перед назначением"
Для следующего кода Python 2.7:
#!/usr/bin/python
def funcA():
print "funcA"
c = 0
def funcB():
c += 3
print "funcB", c
def funcC():
print "funcC", c
print "c", c
funcB()
c += 2
funcC()
c += 2
funcB()
c += 2
funcC()
print "end"
funcA()
Я получаю следующую ошибку:
File "./a.py", line 9, in funcB
c += 3
UnboundLocalError: local variable 'c' referenced before assignment
Но когда я прокомментирую строку c += 3
в funcB
, я получаю следующий вывод:
funcA
c 0
funcB 0
funcC 2
funcB 4
funcC 6
end
Доступен ли c
в обоих случаях +=
в funcB
и =
в funcC
? Почему он не бросает ошибку для одного, но не для другого?
У меня нет выбора сделать глобальную переменную c
, а затем объявить global c
в funcB
. Во всяком случае, дело не в том, чтобы получить c
с приращением в funcB
, а за то, почему он бросает ошибку для funcB
, а не для funcC
, в то время как оба обращаются к переменной, которая является локальной или глобальной.
Ответы
Ответ 1
То, что вы видите здесь, - это разница между доступом и назначением переменных. В Python 2.x вы можете назначать только переменные во внутренней области или глобальной области (последняя выполняется с помощью глобального оператора). Вы можете обращаться к переменным в любой охватывающей области, но вы не можете получить доступ к переменной в охватывающей области и затем назначить ее в самой внутренней или глобальной области.
Это означает, что если есть какое-либо присвоение имени внутри функции, это имя уже должно быть определено в самой внутренней области до того, как будет доступно обращение к имени (если только не использовался глобальный оператор). В вашем коде строка c += 3
по существу эквивалентна следующему:
tmp = c
c = tmp + 3
Поскольку в функции есть назначение c
, любое другое вхождение c
в этой функции будет отображаться только в локальной области для funcB
. Вот почему вы видите ошибку, вы пытаетесь получить доступ к c
, чтобы получить его текущее значение для +=
, но в локальной области c
еще не определено.
В Python 3 вы можете обойти эту проблему, используя нелокальный оператор, который позволяет назначать переменные, которые не находятся в текущей области, но также не в глобальном масштабе.
Ваш код будет выглядеть примерно так, с аналогичной строкой вверху funcC
:
def funcB():
nonlocal c
c += 3
...
В Python 2.x это не вариант, и единственный способ изменить значение нелокальной переменной - это изменить ее.
Самый простой способ сделать это - обернуть ваше значение в списке, а затем изменить и получить доступ к первому элементу этого списка в любом месте, где вы ранее использовали имя переменной:
def funcA():
print "funcA"
c = [0]
def funcB():
c[0] += 3
print "funcB", c[0]
def funcC():
c[0] = 5
print "funcC", c[0]
print "c", c[0]
funcB()
funcC()
funcB()
funcC()
print "end"
funcA()
... и вывод:
funcA
c 0
funcB 3
funcC 5
funcB 8
funcC 5
end
Ответ 2
Не доступен ли "c" в обоих случаях "+ =" в funcB и "=" в funcC?
Нет, funcC
создает новую переменную, также называемую c
. =
в этом отношении отличается от +=
.
Чтобы получить поведение, которое вы (возможно) хотите, оберните переменную вверх в одноэлементный список:
def outer():
c = [0]
def inner():
c[0] = 3
inner()
print c[0]
напечатает 3
.
Изменить. Вы хотите передать c
в качестве аргумента. Python 2 не имеет другого способа, AFAIK, чтобы получить желаемое поведение. Python 3 вводит ключевое слово nonlocal
для этих случаев.
Ответ 3
1) Не доступен ли c
в обоих случаях +=
в funcB и =
в funcC
?
Нет, потому что c += 3
совпадает с:
c = c + 3
^
|
and funcB does not know what this c is
2) У меня нет выбора сделать c
глобальную переменную, а затем объявить global c
в funcB
.
Пожалуйста, не делайте этого, просто измените:
def funcB():
с:
def funcB(c):
и вызовите funcB(c)
позже в вашем коде.
Примечание.. Вы также должны определить funcB
и funcC
вне funcA
Ответ 4
Попробуйте следующее:
def funcA():
print "funcA"
c = 0
def funcB(c):
c += 3
print "funcB", c
def funcC(c):
c = 5
print "funcC", c
print "c", c
funcB(c)
funcC(c)
funcB(c)
funcC(c)
print "end"
funcA()
И если вы хотите запомнить значение c, то:
def funcA():
print "funcA"
c = 0
def funcB(c):
c += 3
print "funcB", c
return c
def funcC(c):
c = 5
print "funcC", c
return c
print "c", c
c = funcB(c)
c = funcC(c)
c = funcB(c)
c = funcC(c)
print "end"
funcA()
который будет производить:
funcA
c 0
funcB 3
funcC 5
funcB 8
funcC 5
end
C:\Python26\
Ответ 5
Другое грязное обходное решение, которое, однако, не требует, чтобы вы сделали c глобальным. Все одинаково, но:
def funcB():
globals()['c'] += 3
print "funcB", c