#python
#python
Вопрос:
Я пишу внутреннюю функцию во внешней функции, затем произошло что-то связанное, то есть при присвоении значения локальной переменной во внутренней функции произошла ошибка UnboundLocalError. И наоборот, если я просто распечатываю локальную переменную во внутренней функции, она работает довольно хорошо. Позвольте мне показать вам упрощенный код. Я знаю, что это имеет отношение к правилам LEGB в Python, но я все еще не мог понять, почему это произошло. Большое спасибо, если кто-нибудь может дать мне некоторые идеи. Позвольте мне показать вам упрощенный код.
def outer1():
number = 10
def inner():
print(number)
inner()
def outer2():
number = 20
def inner():
if number >= 20:
number = 1
inner()
outer1()
функция работает хорошо, но outer2()
функция выдает a UnboundLocalError: local variable 'number' referenced before assignment
.
Я знаю, что использование nonlocal
ключевого слова может решить проблему. но меня все еще что-то смущает. Почему if
оператор в outer2()
функции не искал переменную number
во внешней функции, как это outer1()
сделала функция. Кто-нибудь может дать мне некоторые объяснения по этому поводу?
Комментарии:
1.
number
Во втором случае вы присвоили значениеinner()
within . Это делает его локальной переменнойinner()
. Но вы не присвоили ему никакого значения внутриinner()
, прежде чем пытаться его использовать. В этом случае он не наследует значениеnumber
fromouter()
.
Ответ №1:
nonlocal
В таких случаях необходимо использовать оператор:
def outer2():
number = 20
def inner():
nonlocal number
if number >= 20:
number = 1
inner()
Чтобы понять, почему, давайте посмотрим на байт-код:
import dis
def outer2():
number = 20
def inner():
if number >= 20:
number
inner()
def outer3():
number = 20
def inner():
if number >= 20:
number = number 1
inner()
def outer4():
number = 20
def inner():
nonlocal number
if number >= 20:
number = number 1
inner()
>>> dis.dis(outer2)
2 0 LOAD_CONST 1 (20)
3 STORE_DEREF 0 (number)
3 6 LOAD_CLOSURE 0 (number)
9 BUILD_TUPLE 1
12 LOAD_CONST 2 (<code object inner at 0x7ff003b56b70, file "<stdin>", line 3>)
15 LOAD_CONST 3 ('outer2.<locals>.inner')
18 MAKE_CLOSURE 0
21 STORE_FAST 0 (inner)
6 24 LOAD_FAST 0 (inner)
27 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
30 POP_TOP
31 LOAD_CONST 0 (None)
34 RETURN_VALUE
>>> dis.dis(outer3)
2 0 LOAD_CONST 1 (20)
3 STORE_FAST 0 (number)
3 6 LOAD_CONST 2 (<code object inner at 0x7ff003b56ae0, file "<stdin>", line 3>)
9 LOAD_CONST 3 ('outer3.<locals>.inner')
12 MAKE_FUNCTION 0
15 STORE_FAST 1 (inner)
6 18 LOAD_FAST 1 (inner)
21 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
24 POP_TOP
25 LOAD_CONST 0 (None)
28 RETURN_VALUE
>>> dis.dis(outer4)
2 0 LOAD_CONST 1 (20)
3 STORE_DEREF 0 (number)
3 6 LOAD_CLOSURE 0 (number)
9 BUILD_TUPLE 1
12 LOAD_CONST 2 (<code object inner at 0x7ff003af7e40, file "<stdin>", line 3>)
15 LOAD_CONST 3 ('outer4.<locals>.inner')
18 MAKE_CLOSURE 0
21 STORE_FAST 0 (inner)
7 24 LOAD_FAST 0 (inner)
27 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
30 POP_TOP
31 LOAD_CONST 0 (None)
34 RETURN_VALUE
Из этих примеров видно, что попытка присвоить переменной (которая не была объявлена как нелокальная)
уступает MAKE_FUNCTION
коду операции. Но только замыкания позволяют получить доступ к переменным из текущей области.
Вы также можете прочитать больше о замыканиях здесь .
Примечание: Это было протестировано в Python 3.5; Также читайте об изменениях MAKE_FUNCTION
и MAKE_CLOSURE
кодах операций в python 3.6
Комментарии:
1. Я знаю, что использование нелокального ключевого слова может решить проблему. но меня все еще что-то смущает. Почему оператор if в функции outer2() не искал переменную
number
во внешней функции, как это делала функция outer1() . Не могли бы вы дать мне несколько объяснений по этому поводу?2. Большое спасибо. Вы заставляете меня полностью понять это.
Ответ №2:
def outer2():
number = 20
def inner():
nonlocal number # this is the additional change required
if number >= 20:
number = 1
inner()
В функциях python можно получить доступ к «глобальным» переменным внутри функций и даже их методов, но не присваивать им значения, потому что тогда функция ищет свою локальную копию переменной.
Ошибка заключается в том, что функция ищет локальную переменную ‘number’ в записи таблицы символов и не находит ее.
Обычно это можно решить с помощью ключевого слова global, чтобы указать, что редактируемая переменная имеет глобальную область видимости, но поскольку функция определена в другой функции, вам нужно будет использовать ключевое слово ‘nonloc’, которое является другим способом сказать, что переменная, которую вы хотите изменить, не является локальной дляэтой функции, но функции, в которой она определена, которая в данном случае является outer2 .