Возможно ли получить доступ к exec-предоставленному глобальному словарю из функции? - PullRequest
0 голосов
/ 02 ноября 2018

Можно ли получить доступ к глобальному словарю, предоставленному exec, из функции, если функция была определена вне кода exec-ed (и, таким образом, уже связана с другим __globals__)?

Другими словами, есть ли способ заставить следующий пример работать?

def f():
    log("Hi")

exec('f()', {'f': f, 'log': print})

В общем, возможно ли заменить __globals__ функции?

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

Это довольно странная вещь, но она выполнима.

Ваш вызов exec выполняет оператор f() в предоставленных глобальных переменных. Он не выполняет body из f в предоставленных глобальных переменных. Предоставленные глобальные переменные используются в неправильном кадре стека. Чтобы получить доступ к этим глобальным переменным из f, вы можете использовать проверка стека :

import inspect

def f():
    log = inspect.currentframe().f_back.f_globals['log']
    log('Hi')

exec('f()', {'f': f, 'log': print})

Если вы хотите выполнить тело f с предоставленными глобалами, а не просто получить доступ к глобалам, вам нужно сделать копию f с вашими собственными глобальными глобалами:

import types
my_f = types.FunctionType(f.__code__,
                          {'log': print},
                          f.__name__,
                          f.__defaults__,
                          f.__closure__)
my_f()

Конструктор типа функции является своего рода документированным; его нет в онлайн-документах, но задокументировано в строке документации типа функции:

function(code, globals[, name[, argdefs[, closure]]])

Create a function object from a code object and a dictionary.
The optional name string overrides the name from the code object.
The optional argdefs tuple specifies the default argument values.
The optional closure tuple supplies the bindings for free variables.
0 голосов
/ 04 ноября 2018

Не уверен, что я полностью прав насчет объяснения. Короче говоря, пример не может работать в Python 3.

Причина в комбинации двух обстоятельств: [1] - exec - это функция в Python 3, [2] - код, который вы пытаетесь выполнить, содержит вызов функции.

Когда вы предоставляете globals необязательный аргумент для функции exec, это локальная область действия этой самой функции. Так работает следующий пример:

exec('log("Hi")', {'log': print})

Но оригинал - нет. Потому что в исходном примере вы вызываете функцию f. У него есть свой локальный охват. Что делает Python? Он проверяет глобальную область (действительную глобальную область программы) и самую внутреннюю область (локальная область функции f). Для обеих областей не хватает log, и вы получаете NameError.

Вы можете получить то же самое поведение (ту же ошибку) с двумя обычными функциями:

def f():
    log("Hi")


def f_():
    log = print
    f()

f_()
...