我可以定义一个函数,在调用时,将新的本地插入到调用者的范围中吗?我有一种感觉,将调用者的 locals()传递给函数可能会有效,但有没有办法做我想要的而不必这样做?
答案 0 :(得分:18)
查看inspect module,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'
答案 1 :(得分:8)
根据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
访问语法。
答案 2 :(得分:0)
如果不更改API,这是不可能的,因为CPython在函数调用期间保留对参数列表的引用。
执行此操作的一种方法是传递一个对象,并在使用它时清除对象的内部状态。
class Obj(object):
def __init__(self, state):
self.state = state
def f(o):
# Do stuff with o.state
del o.state