Где нелокальные ()?
Как получить нелокальные переменные для текущей области? Функции vars
, locals
, а также globals
существует, но есть ли функция, чтобы получить nonlocals
?
Почему нелокалы не указаны при звонке vars
?
Обновить
Моя проблема в том, что нет способа перечислить переменные, доступные в текущей области, так как ни vars
или же globals
включает неместных AFAICT.
Я часто использую vars
в коде, таком как следующее:
'{meh[0]}/{meh[3]} {a}{b}{c}'.format(**vars())
Что не удается, если какая-либо из этих переменных находится в области действия содержащей функции.
1 ответ
Внутри исполняемого кода вы можете легко получить имена нелокальных переменных, но получить их содержимое таким образом, чтобы locals
получить вам словарь немного сложнее.
Используется nonlocal
имена переменных хранятся в текущем объекте кода, в атрибуте co_freevars.
Итак, получение нелокальных имен - это вопрос:
names = inspect.currentframe().f_code.co_freevars
Однако содержимое этих переменных хранится в __closure__
атрибут (func_closure
вместо этого в Python 2) объекта функции. (Не кодовый объект). Проблема заключается в том, что без "помощи извне" для работающего кода нет простого способа добраться до объекта функции, на котором он выполняется. Вы можете получить к объекту frame, который ссылается на объект кода, но нет ссылок на объект функции. (Для функции, определенной на верхнем уровне, можно явно использовать имя функции, используемое в def
Statement`, но для вложенной функции, которая возвращается вызывающей стороне, также нет способа узнать ее имя).
Таким образом, нужно прибегнуть к хитрости - получить все объекты, которые ссылаются на текущий объект кода, с помощью модуля gc (сборщик мусора) - существует gc.get_referrers
call - он вернет все объекты функций, которые ссылаются на объект кода, который он содержит.
Итак, внутри функции с нелокальными переменными можно сделать:
import inspect, gc
def a(b):
b1 = 2
def c():
nonlocal b1
print (b)
code = inspect.currentframe().f_code
names = code.co_freevars
function = [func for func in gc.get_referrers(code) if isinstance(func, FunctionType)][0]
nonlocals = dict (zip(names, (x.cell_contents for x in function.__closure__ )))
print(nonlocals)
return inspect.currentframe()
return c
c = a(5)
f = c()
И, следовательно, получить имена и значения нелокальных. Но это не сработает, если у вас есть более одного экземпляра этой функции (то есть, если интересующая функция была создана более одного раза с более чем одним вызовом функции, которая ее генерирует) - потому что все эти экземпляры будут ссылка на тот же объект кода. В приведенном выше примере предполагается, что с текущим кодом работает только одна функция, и в этом случае она будет работать правильно. Другой вызов функции factrory создаст другую функцию с потенциально другими значениями для нелокальных переменных, но с тем же объектом кода - function =
Приведенный выше список Генератор извлекает все из них и произвольно выбирает первый из них.
"Правильная" функция - это та, на которой выполняется текущий код - я пытаюсь найти способ получения этой информации, но не могу получить ее. Если я смогу, я завершу этот ответ, но сейчас это не поможет вам получить значения нелокальных значений.
(только что выяснилось, что попытка использовать "eval" с нелокальным именем переменной тоже не сработает)
Похоже, что единственное, что связывает текущий рабочий кадр с объектом функции, в котором хранятся значения нелокальных переменных, создается во время выполнения внутри нативной части интерпретатора Python. Я не могу придумать, как можно обойтись без использования модуля ctypes для просмотра структур данных интерпретаторов во время выполнения, что, конечно, не подходит для любого реального производственного кода.
Суть: вы можете надежно получить нелокальные имена переменных. Но похоже, что вы не можете получить их значение, учитывая их имя в виде строки (и не привязывать потом).
Вы можете попробовать открыть запрос функции для вызова "нелокальных" в трекере ошибок Python или в списке рассылки Python-ideas.