将变量注入调用者的范围?

时间:2010-03-25 12:37:45

标签: python variables scope

我可以定义一个函数,在调用时,将新的本地插入到调用者的范围中吗?我有一种感觉,将调用者的 locals()传递给函数可能会有效,但有没有办法做我想要的不必这样做?

3 个答案:

答案 0 :(得分:18)

查看inspect moduleminimock使用它来模拟来电者的范围。

这段代码应该完全符合您的要求:

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