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

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

4 голосов
/ 23 января 2012

Как получить нелокальные переменные для текущей области? Функции vars, locals и globals существуют, но есть ли функция для получения nonlocals?

Почему нелокалы не указаны при звонке vars?

Обновление

Моя проблема в том, что нет способа перечислить переменные, доступные в текущей области, так как ни vars, ни globals не включают нелокальные значения AFAICT.

Я часто использую vars в коде, таком как следующее:

'{meh[0]}/{meh[3]} {a}{b}{c}'.format(**vars())

Что не срабатывает, если какая-либо из этих переменных находится в области действия содержащей функции.

1 Ответ

6 голосов
/ 23 января 2012

Из запущенного кода вы можете легко получить имена нелокальных переменных - но получение их содержимого таким образом, что вызов 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...