故意将变量放入调用者范围的函数

时间:2012-08-06 08:48:46

标签: python variables scope metaprogramming

我有一个函数P()。致load_variables()的调用应该为P变量xload_variables应该能够接受默认值作为关键字参数。

如何做到这一点?

我尝试了以下内容:

import inspect
def P():
    x = 1
    load_variables(x = 2)
    return x

def load_variables(**kargs):
    stack = inspect.stack()
    try:
        locals_ = stack[1][0].f_locals
    finally:
        del stack
    for __k, __v in kargs.iteritems():
        locals_[__k] = __v

print P() # => should print 2

x = 1行实际上并不存在,因为我希望load_variables()x放到P的范围内。

还有另一种,或许更好的方法吗?我想要的是:

  1. 变量具有默认值,例如上述x = 2的调用load_variables()
  2. 我可以在load_variables覆盖这些内容,例如,load_varibales()可以访问变量字典,如果x已经在这里,我们会覆盖它,并将其溢出{{ 1}}而不是作为默认参数给出的那个。

3 个答案:

答案 0 :(得分:1)

更新:正如DSM指出的那样,不支持更新locals()。所以,答案是不正确的,至少对于CPython来说。

你能让load_variables()返回一个字典并使用locals()来更新局部变量:

def P():
    x=1
    locals().update(load_variables(x=2))
    return x

def load_variables(**kargs):
    res={}
    for __k, __v in kargs.iteritems():
        res[__k] = __v
    return res

请注意,x=1是必需的,否则x将被字节码编译器假定为全局变量,NameError将获得return x异常

<击>

如果你真的想要摆脱x=1,你可以改为retrun locals()['x']

答案 1 :(得分:1)

Python编译器和字节码解释器在可能的情况下处理对作为定义大小数组的插槽的局部变量的引用。这意味着如果局部变量未在作用域中初始化(赋值),则语言将不知道作用域中存在该变量的槽,而是在封闭作用域或全局作用域中查找该变量。查看函数P的反汇编:

def P():
    load_variables(x=2)
    return x

dis.dis(P)
  2           0 LOAD_GLOBAL              0 (load_variables)
              3 LOAD_CONST               1 ('x')
              6 LOAD_CONST               2 (2)
              9 CALL_FUNCTION          256
             12 POP_TOP             

  3          13 LOAD_GLOBAL              1 (x)
             16 RETURN_VALUE        

您可以看到,如果未在本地范围内x分配P,则会在全局范围内查找。

执行此操作的正确方法是使用解包显式声明您希望load_variables函数返回哪些变量:

x, y, z = load_variables(...)

答案 2 :(得分:1)

由于我们在这里谈论Python,可能有一种方法可以做到,但我想知道:你应该尝试吗?

通常,定义共享存储/值对象然后传递它更好:

data = {}

def P(data):
     load_variables(data, x=2)
     return data['x']

def load_variables(data, **kargs):
     data.update(kargs)

这样,你就不会有不可预见的副作用。