Python 2.x中的非本地关键字

我试图在Python 2.6中实现一个闭包,我需要访问一个非本地variables,但似乎这个关键字在python 2.x中不可用。 在这些版本的python中,应该如何访问非本地variables?

Python可以在2.x中读取非本地variables,只是不能改变它们。 这很烦人,但你可以解决它。 只需声明一个字典,并将其中的variables存储为元素。

要使用维基百科的例子:

def outer(): d = {'y' : 0} def inner(): d['y'] += 1 return d['y'] return inner f = outer() print(f(), f(), f()) #prints 1 2 3 

下面的解决scheme是由Elias Zamaria的答案所启发的,但与此相反的是,它正确地处理了外部函数的多个调用。 对于当前的outer调用,“variables” inner.y是本地的。 只有它不是一个variables,因为这是被禁止的,而是一个对象属性(对象是inner的函数)。 这是非常丑陋的(请注意,该属性只能在定义inner函数之后创build),但看起来有效。

 def outer(): def inner(): inner.y += 1 return inner.y inner.y = 0 return inner f = outer() g = outer() print(f(), f(), g(), f(), g()) #prints (1, 2, 1, 3, 2) 

而不是一本字典,对于一个非本地的类来说 ,就不那么混乱了。 修改@ ChrisB的例子 :

 def outer(): class context: y = 0 def inner(): context.y += 1 return context.y return inner 

然后

 f = outer() assert f() == 1 assert f() == 2 assert f() == 3 

每个outer()调用都会创build一个单独的上下文。 所以它避免了纳撒尼尔的提防 。

 g = outer() assert g() == 1 assert g() == 2 assert f() == 4 

我认为这里的关键是“访问”的意思。 在closures范围之外读取一个variables应该没有问题,例如,

 x = 3 def outer(): def inner(): print x inner() outer() 

应按预期工作(打印3)。 但是,压倒x的价值是行不通的,例如,

 x = 3 def outer(): def inner(): x = 5 inner() outer() print x 

仍然会打印3.根据我对PEP-3104的理解,这是非本地关键字的意图。 正如在PEP中提到的,你可以使用一个类来完成同样的事情(很麻烦):

 class Namespace(object): pass ns = Namespace() ns.x = 3 def outer(): def inner(): ns.x = 5 inner() outer() print ns.x 

还有另外一种方法可以在Python 2中实现nonlocalvariables,以防出于任何原因,这里的任何答案都是不可取的:

 def outer(): outer.y = 0 def inner(): outer.y += 1 return outer.y return inner f = outer() print(f(), f(), f()) #prints 1 2 3 

在variables的赋值语句中使用函数的名字是多余的,但是与将variables放在字典中相比,它看起来更简单和更清晰。 就像在克里斯·B的答案中一样,从一个呼叫到另一个呼叫都记住了这个值。

这里的一些启发是由阿洛伊斯·马赫达尔(Alois Mahdal)在一个关于另一个答案的评论中提出的:

 class Nonlocals(object): """ Helper class to implement nonlocal names in Python 2.x """ def __init__(self, **kwargs): self.__dict__.update(kwargs) def outer(): nonlocals = Nonlocals(y=0) def inner(): nonlocals.y += 1 return nonlocals.y return inner f = outer() print(f(), f(), f()) # -> (1 2 3) 

另一种方式来做到这一点(尽pipe它太冗长了):

 import ctypes def outer(): y = 0 def inner(): ctypes.pythonapi.PyCell_Set(id(inner.func_closure[0]), id(y + 1)) return y return inner x = outer() x() >> 1 x() >> 2 y = outer() y() >> 1 x() >> 3 

python的作用域规则中有一个疣 – 赋值给它的立即封闭函数作用域的局部variables。 对于一个全局variables,你可以用global关键字来解决这个问题。

解决的办法是引入一个在两个作用域之间共享的对象,它包含可变variables,但是它本身是通过一个未被赋值的variables来引用的。

 def outer(v): def inner(container = [v]): container[0] += 1 return container[0] return inner 

一个替代的是一些示波器hackery:

 def outer(v): def inner(varname = 'v', scope = locals()): scope[varname] += 1 return scope[varname] return inner 

你可能会弄清楚一些技巧来获取参数的名字到outer ,然后把它作为varname传递,但是不依赖于outer的名字,你需要使用一个Y组合器。

将Martineau优雅的解决scheme扩展到一个实用的,不太好用的用例,我得到:

 class nonlocals(object): """ Helper to implement nonlocal names in Python 2.x. Usage example: def outer(): nl = nonlocals( n=0, m=1 ) def inner(): nl.n += 1 inner() # will increment nl.n or... sums = nonlocals( { k:v for k,v in locals().iteritems() if k.startswith('tot_') } ) """ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __init__(self, a_dict): self.__dict__.update(a_dict) 

使用全局variables

 def outer(): global y # import1 y = 0 def inner(): global y # import2 - requires import1 y += 1 return y return inner f = outer() print(f(), f(), f()) #prints 1 2 3 

就个人而言,我不喜欢全局variables。 但是,我的build议是基于https://stackoverflow.com/a/19877437/1083704的答案;

 def report(): class Rank: def __init__(self): report.ranks += 1 rank = Rank() report.ranks = 0 report() 

用户需要声明一个全局variablesranks ,每次需要调用该report 。 我的改进消除了从用户初始化函数variables的需要。