Полный список возможных полей в форме создания электронной почты, основанной на html:
Когда вы не знаете, почему что-то работает в Python, оно часто может помочь поместить поведение, которое вас путает в функции, а затем разобрать его из байт-кода Python с помощью модуля dis
.
Давайте начнем с более простой версии вашего кода:
def foo():
exec("K = 89")
print(K)
Если вы запустите foo()
, вы получите то же самое исключение, которое вы видите с более сложными function:
>>> foo()
Traceback (most recent call last):
File "<pyshell#167>", line 1, in <module>
foo()
File "<pyshell#166>", line 3, in foo
print(K)
NameError: name 'K' is not defined
Давайте разобраем его и посмотрим, почему:
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (exec)
3 LOAD_CONST 1 ('K = 89')
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 POP_TOP
3 10 LOAD_GLOBAL 1 (print)
13 LOAD_GLOBAL 2 (K)
16 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
19 POP_TOP
20 LOAD_CONST 0 (None)
23 RETURN_VALUE
Операция, на которую вы должны обратить внимание, - это символ «13». Здесь компилятор обрабатывает поиск K
в последней строке функции (print(K)
). Он использует код операции LOAD_GLOBAL
, который терпит неудачу, потому что «K» не является глобальным именем переменной, а является значением в нашем locals()
dict (добавлено вызовом exec
).
Что если мы уговорили компилятор видеть K
как локальную переменную (давая ей значение перед запуском exec
), поэтому он будет знать, что не искать глобальную переменную, которая не существует?
def bar():
K = None
exec("K = 89")
print(K)
Эта функция не даст вам ошибки, если вы ее запустите, но вы не получите ожидаемое значение:
>>> bar()
None
Давайте разобрать, чтобы понять, почему:
>>> dis.dis(bar)
2 0 LOAD_CONST 0 (None)
3 STORE_FAST 0 (K)
3 6 LOAD_GLOBAL 0 (exec)
9 LOAD_CONST 1 ('K = 89')
12 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
15 POP_TOP
4 16 LOAD_GLOBAL 1 (print)
19 LOAD_FAST 0 (K)
22 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
25 POP_TOP
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
Обратите внимание на коды операций, используемые в «3» и «19». Компилятор Python использует STORE_FAST
и LOAD_FAST
, чтобы поместить значение для локальной переменной K
в слот 0 и позже, чтобы вернуть его обратно. Использование пронумерованных слотов значительно быстрее, чем вставка и выборка значений из словаря, подобного locals()
, поэтому компилятор Python делает это для всех локальных переменных доступа в функции. Вы не можете перезаписать локальную переменную в слоте, изменив словарь, возвращенный locals()
(как это делает exec
, если вы не передадите ему dict для использования в своем пространстве имен).
В самом деле, давайте попробуем третий вариант нашей функции, где мы снова заглянем в locals
, когда у нас K
определяется как регулярная локальная переменная:
def baz():
K = None
exec("K = 89")
print(locals())
Вы не увидите 89
на выходе это тоже!
>>> baz()
{"K": None}
Причина, по которой вы видите старое значение K
в locals()
, объясняется в документации функции :
Обновить и вернуть словарь, представляющий текущую таблицу локальных символов.
blockquote> Слот, в котором хранится значение локальной переменной K
, не было изменено exec
, который только модифицирует locals()
dict. Когда вы снова вызываете locals()
, Python «обновляет [s]» словарь со значением из слота, заменяя значение, хранящееся там на exec
.
Вот почему документы продолжаются, чтобы сказать :
Примечание. Содержимое этого словаря не должно изменяться; изменения могут не влиять на значения локальных и свободных переменных, используемых интерпретатором.
blockquote>Ваш вызов
exec
изменяет dictlocals()
, и вы обнаруживаете, как его изменения не являются всегда видно вашим более поздним кодом.
В вопросе exec / eval / locals
По крайней мере, в реализации CPython модификации словаря locals()
фактически не изменяют имена в локальной области, которые поэтому он предназначен для использования только для чтения. Вы можете изменить его, и вы можете увидеть свои изменения в объекте словаря, но фактическая локальная область не изменяется.
exec()
принимает два необязательных словарных аргумента, глобальную область действия и локальную область. По умолчанию это globals()
и locals()
, но поскольку изменения в locals()
не являются «реальными» вне словаря, exec()
влияет только на «реальную» локальную область, когда globals() is locals()
, т.е. в модуле вне любой функции. (Так что в вашем случае это происходит из-за того, что оно находится внутри области функций).
«Лучшим» способом использования exec()
в этом случае является передача в ваш собственный словарь, а затем работа с значениями в этом .
def foo():
exec_scope = {}
exec("y = 2", exec_scope)
print(exec_scope['y'])
foo()
В этом случае exec_scope
используется как глобальная и локальная область для exec
, а после exec
она будет содержать {'y': 2, '__builtins__': __builtins__}
(встроенные встроены для вы, если не присутствуете)
Если вы хотите получить доступ к более глобальным значениям, вы можете сделать exec_scope = dict(globals())
.
Передача в разных словарях глобального и локального масштаба может привести к «интересному» поведению.
Если вы передаете один и тот же словарь в последовательные вызовы exec
или eval
, то они имеют одинаковую область действия, поэтому ваш eval
работал (он неявно использовал словарь locals()
).
В именах динамических переменных
Если вы задаете имя из строки, что не так, чтобы получить значение как строку (то есть, что делает словарь)? Другими словами, почему вы хотите установить locals()['K']
, а затем получить доступ к K
? Если K
находится в вашем источнике, это не действительно динамически заданное имя ... следовательно, словари.
locals()
не гарантированы и действительно происходят в других Pythons (я думаю, что IronPython и Java Python делают это).
– Ethan Furman
8 August 2014 в 02:31
locals()
не является указанным языком, поэтому «не может» & quot; формулировка.
– Ethan Furman
8 August 2014 в 08:56