Почему пространство имён для eval() так работает?
Функция grid выдаёт ошибку name 'row' is not defined, так как функция eval вызывается внутри вызова grid. Однако, в функции place такой проблемы нет, хотя команды те же. Почему eval внутри вызова функции меняет свое пространство имён?
import tkinter as tk
def _parametrize(widget, method, kvs):
s = "widget.%s(" % method
r = []
for u, v in kvs:
if v != None:
r += ["%s = %s" % (u, v)]
s += ", ".join(r) + ")"
exec(s)
def place(widget, x = None, y = None, width = None, height = None):
kvs = []
for s in ("x", "y", "width", "height"):
kvs += [(s, eval(s))]
_parametrize(widget, "place", kvs)
def grid(widget, row = None, column = None, rowspan = None, columnspan = None):
_parametrize(widget, "grid", [(s, eval(s)) for s in ("row", "column", "rowspan", "columnspan")])
r = tk.Tk()
b = Button(r, text = "Button")
place(b, 20, 20, 100, 50)
grid(b)
r.mainloop()
Ответы (1 шт):
В качестве области видимости eval использует либо глобальную область видимости (если запущен вне функции), либо локальную область видимости (если запущен внутри функции).
У вас как раз случай с локальной областью видимости.
Однако, следует учитывать, что list comprehension создаёт новую локальную область видимости.
В этом легко убедиться:
def foo():
a = 1
print(locals())
# Выведет: {'a': 1}
[print(locals()) for x in range(1)]
# Выведет: {'x': 0, '.0': <range_iterator object at 0x7fb02c9288a0>}
foo()
Как видите, в первом случае в локальной области видимости есть переменная a. А во втором - её нет, но есть переменная x.
Чтобы ваш код работал, ему можно явно передать области видимости:
def grid(widget, row = None, column = None, rowspan = None, columnspan = None):
globs = globals()
locs = locals()
_parametrize(widget, "grid", [(s, eval(s, globs, locs))
for s in ("row", "column", "rowspan", "columnspan")])
Но, как уже правильно сказал insolor, не надо использовать exec и eval без очень серьёзной на то причины. А в вашем случае легко можно обойтись без них.