Из запущенного кода вы можете легко получить имена нелокальных переменных - но получение их содержимого таким образом, что вызов locals
дает вам словарь, немного сложнее.
Используемые nonlocal
имена переменных сохраняются в текущем объекте исполняемого кода в атрибуте co_freevars.
Итак, получение нелокальных имен - это вопрос:
names = inspect.currentframe().f_code.co_freevars
Содержимое для этих переменных, однако, сохраняется вместо этого в атрибуте __closure__
(func_closure
, в Python 2) функционального объекта . (Не кодовый объект). Проблема заключается в том, что без «помощи извне» для работающего кода нет простого способа добраться до объекта функции, на котором он выполняется. Вы можете получить к объекту frame, который ссылается на объект кода, но нет ссылок на объект функции. (Для определенной на верхнем уровне функции можно явно использовать известное имя функции, как в операторе def
, но для закрытой функции, которая возвращается вызывающей стороне, также нет способа узнать ее имя).
Итак, нужно прибегнуть к хитрости - получить все объекты, которые ссылаются на текущий объект кода, с помощью модуля gc (сборщик мусора) - есть вызов gc.get_referrers
- он вернет все функциональные объекты эта ссылка на объект кода, который каждый держит.
Итак, внутри функции с нелокальными переменными можно сделать:
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.