Вводить переменные в область действия вызывающего? - PullRequest
15 голосов
/ 25 марта 2010

Могу ли я определить функцию, которая при вызове вставляет новые локальные объекты в область вызова? У меня такое ощущение, что передача locals () вызывающей функции в функцию может сработать, но есть ли способ сделать то, что я хочу без необходимости делать это?

Ответы [ 3 ]

18 голосов
/ 11 августа 2011

Проверьте проверяющий модуль , он используется minimock для проверки области действия вызывающего абонента.

Этот код должен делать то, что вы хотите точно:

import inspect
def mess_with_caller():
    stack = inspect.stack()
    try:
        locals_ = stack[1][0].f_locals
    finally:
        del stack
    locals_['my_new_function'] = lambda : 'yaaaaaay'


mess_with_caller()
print my_new_function()
>>> Output: 'yaaaaaay'
8 голосов
/ 25 марта 2010

По правилам Python вы не можете изменить locals вашего абонента; в текущих реализациях, если вы попытаетесь (например, с помощью черной магии, которую предлагает Anurag), вы не получите исключение (хотя я бы хотел добавить эту проверку ошибок в какую-то будущую версию), но это будет по существу неработоспособно, если ваш вызывающий функция (не если ваш вызывающий является кодом верхнего уровня модуля) - фактические локальные переменные вызывающего фактически не будут затронуты. Это верно, независимо от того, переданы ли locals вызывающего в явном виде или извлечены с помощью черной магии: они все равно должны рассматриваться как диктовка только для чтения, если ваш код должен иметь какой-либо здравый смысл.

Скорее, вы могли бы передать вызывающей стороне явный, настоящий, нормальный dict (который можно инициализировать с locals(), если хотите), и все изменения, которые ваш код делает в этом dict, все еще будут доступны для использования вызывающей стороной. - конечно, не как «новые имена» в локальной области вызова, но функциональность остается той же, независимо от того, нужно ли вызывающему использовать x['foo'] или x.foo или (как вы предпочитаете) просто имя foo.

Кстати, чтобы использовать синтаксис доступа к атрибутам вместо синтаксиса индексации dict, вы можете сделать:

class Bunch(object): pass

...

# caller code
b = Bunch()
thefun(b)
print b.foo

...

# called function
def thefun(b):
  b.foo = 23

Это также с небольшим изменением охватывает случай, когда thefun хочет работать с синтаксисом индексации dict (скажем, его тело b['foo'] = 23 вместо b.foo = 23): в этом случае вызывающему просто нужно используйте thefun(vars(b)) вместо простого thefun(b), но он может продолжать работать с синтаксисом доступа b.foo.

0 голосов
/ 08 января 2018

Это невозможно без изменения API, поскольку CPython сохраняет ссылку на список аргументов на время вызова функции.

Один из способов сделать это - передать объект и очистить внутреннее состояние объекта после его использования.

class Obj(object):
    def __init__(self, state):
        self.state = state

def f(o):
    # Do stuff with o.state
    del o.state
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...