Где нелокальные ()?

Как получить нелокальные переменные для текущей области? Функции 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.

Другие вопросы по тегам