Могу ли я определить функцию, которая при вызове вставляет новые локали в область вызова? У меня такое чувство, что передача locals() в функцию может работать, но есть ли способ сделать то, что я хочу, не делая этого?
Внедрение переменных в область вызова?
Ответ 1
По правилам Python вы не можете изменить свой вызывающий locals
; в текущих реализациях, если вы попробуете (например, с черной магией Анураг предлагает), вы не получите исключение (хотя я бы хотел добавить эту проверку ошибок в какую-то будущую версию), но она будет по сути недействительна, если ваш вызывающий функция (не если ваш вызывающий является модулем верхнего уровня модуля) - фактические локальные переменные вызывающего абонента фактически не будут затронуты. Это означает, что вызывающий locals
явно передан или извлечен черной магией: их все равно нужно рассматривать как только для чтения, если ваш код должен иметь какое-либо здравомыслие.
Скорее, вы могли бы передать вызывающий код в явном, реальном, нормальном dict (который может быть инициализирован из locals()
, если вы хотите), и все изменения, которые ваш код делает в этом dict, все равно будут использоваться для использования вызывающего абонента - просто не как "новые барные имена" в области локального вызова, конечно, но функциональность одинакова, нужно ли вызывающему абоненту использовать x['foo']
или x.foo
или (как вы предпочитаете) просто barename foo
.
BTW, чтобы использовать синтаксис атрибутного доступа, а не синтаксис индексации, вы можете сделать:
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
.
Ответ 2
Проверьте модуль проверки, он используется 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'
Ответ 3
Это невозможно без изменения API, поскольку CPython сохраняет ссылку на список аргументов в течение продолжительности вызова функции.
Один из способов сделать это - передать объект и очистить внутреннее состояние объекта, когда он будет выполнен с его помощью.
class Obj(object):
def __init__(self, state):
self.state = state
def f(o):
# Do stuff with o.state
del o.state