在Python 2.x的外地关键字

问题描述 投票:109回答:10

我想实现在Python 2.6,关闭和我需要访问一个非本地变量,但它似乎像这个关键字是不是在Python 2.x中可用一个人应该如何访问非本地变量在关闭这些版本的Python?

python closures python-2.x python-nonlocal
10个回答
116
投票

内部函数可以在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

-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。我改进不需要从用户初始化函数变量。


37
投票

下面的解决方案是由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)

29
投票

而不是一本字典,还有杂乱的外地类少。修改@ 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

14
投票

我认为,这里的关键是你所说的“访问”。不应该有任何问题与阅读封闭范围之外的变量,例如,

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

12
投票

还有就是要实现在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.的答案。


11
投票

这里的东西被建议阿洛伊斯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两个工作。


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组合。


3
投票

另一种方式来做到这一点(虽然它太冗长):

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

0
投票

扩展上面实际和少了几分优雅的使用情况下,我得到马蒂诺优雅的解决方案:

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)
© www.soinside.com 2019 - 2024. All rights reserved.