2012-08-06 30 views
3

我有一个函数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()只是流血xP的范围。

是否有另一种方法可能更好?我想要的是:

  1. 变量有一个默认值,例如, x = 2在上面的电话load_variables()
  2. 我可以覆盖这些在load_variables,例如,load_varibales()有权访问变量字典,如果x已经在这里,我们覆盖它,并且溢出这个x而不是给定的默认参数。

回答

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(...) 
+0

我如何保证参数的顺序与返回时相同?例如。如@Mohammad Ali写道,'print load_variables(x = 2,y = 4,z = 10)'返回为'{'y':4,'x':2,'z':10}' – 2012-08-06 09:31:10

+0

@ MadsOhmLarsen你必须将它们返回为'return x,y,z'。 – ecatmur 2012-08-06 09:33:57

+0

关键字参数是一个字典,所以它不应该关心顺序,这就是为什么我得到上面的混合回报。是否有可能获得它们被输入到'load_variables'的顺序,以便我可以按照相同的顺序返回未知数量的关键字参数? – 2012-08-06 09:55:15

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

不幸的是,这不起作用:您不能像[文档警告](http://docs.python.org/library/functions.html#locals)这样更新'locals'字典。由于实现相关的原因,'P'在CPython和PyPy中返回1,在IronPython中返回2。 – DSM 2012-08-06 09:28:33

+0

@DSM,我错过了文档中的那一点。谢谢。 – 2012-08-06 09:35:05

1

因为我们正在谈论的Python在这里,有可能是一个办法做到它,但我想知道:你应该尝试吗?

通常,更好的做法是定义一个共享的存储/值对象,然后通过围绕:

data = {} 

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

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

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

+0

+1把“出血”的想法变成了一个更好的范围似乎有点血腥 – msw 2012-08-06 12:19:10