我想实现在Python 2.6,关闭和我需要访问一个非本地变量,但它似乎像这个关键字是不是在Python 2.x中可用一个人应该如何访问非本地变量在关闭这些版本的Python?
内部函数可以在2.x的阅读外地变量,只是没有重新绑定他们。这是恼人的,但你可以解决它。只要创建一个字典,您的数据存储在其中的元素。内在功能都没有从突变所非本地变量指向的对象禁止。
要使用维基百科的例子:
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
使用全局变量
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
就个人而言,我不喜欢的全局变量。但是,我的建议是基于https://stackoverflow.com/a/19877437/1083704答案
def report():
class Rank:
def __init__(self):
report.ranks += 1
rank = Rank()
report.ranks = 0
report()
其中,用户需要声明一个全局变量ranks
,每次需要调用report
。我改进不需要从用户初始化函数变量。
下面的解决方案是由answer by Elias Zamaria启发,但违背了这个问题的答案不正确处理外多个函数调用。在“变量” inner.y
是本地outer
的当前呼叫。只有它不是一个可变的,因为这是禁止的,但对象属性(对象是所述函数inner
本身)。这是非常丑陋的(注意,inner
函数定义后的属性只能创建),但似乎有效。
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的example:
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
assert f() == 4
每个外()调用创建一个新的和独特的类称为上下文(不仅仅是一个新的实例)。因此,它避免了@Nathaniel's beware关于共享上下文。
g = outer()
assert g() == 1
assert g() == 2
assert f() == 5
我认为,这里的关键是你所说的“访问”。不应该有任何问题与阅读封闭范围之外的变量,例如,
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非本地变量的另一种方式,在任何情况下,这里的答案是不可取无论出于何种原因:
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
它是多余的使用变量的赋值语句的函数的名称,但它看起来简单和清晰对我来说比把变量的字典。该值想起了一个电话到另一个,就像克里斯B.的答案。
这里的东西被建议阿洛伊斯Mahdal就其他comment一个answer制成的启发:
class Nonlocal(object):
""" Helper to implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def outer():
nl = Nonlocal(y=0)
def inner():
nl.y += 1
return nl.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
更新
回首在这家后,我被击中如何装饰,好像是,当我恍然大悟,这是其实现为一个将使它更通用和有用(虽然这样做无疑会降低其可读性在一定程度上)。
# Implemented as a decorator.
class Nonlocal(object):
""" Decorator class to help implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self._vars = kwargs
def __call__(self, func):
for k, v in self._vars.items():
setattr(func, k, v)
return func
@Nonlocal(y=0)
def outer():
def inner():
outer.y += 1
return outer.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
请注意,这两个版本在Python 2和3两个工作。
在Python作用域规则疣 - 分配使得局部变量它直接包含的功能范围。对于一个全局变量,你将与global
关键字解决这个问题。
的解决方案是引入在这两个范围,其包含可变的变量之间共享的对象,但它本身通过图中未分配一个变量引用。
def outer(v):
def inner(container = [v]):
container[0] += 1
return container[0]
return inner
另一种是一些范围两轮牛车:
def outer(v):
def inner(varname = 'v', scope = locals()):
scope[varname] += 1
return scope[varname]
return inner
你也许可以找出一些技巧把参数outer
的名称,然后把它作为varname的,但不依赖于名称outer
你想需要使用Y组合。
另一种方式来做到这一点(虽然它太冗长):
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
扩展上面实际和少了几分优雅的使用情况下,我得到马蒂诺优雅的解决方案:
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)