在python中关闭。我可以在函数的本地上下文中进行闭包吗?

时间:2013-11-05 21:25:08

标签: javascript python closures

在javascript中我可以用这样的闭包写函数

function getUniqueIDfunction() { 
    var id = 0;                          
    return function() { return id++; }; 
};

然后使用它

uniqueID = getUniqueIDfunction();
uniqueID(); //return 0
uniqueID(); //return 1
...

我可以在Python中执行相同的操作(如果它取决于不同的版本,请告诉我)?

def getUniqueIDfunction():
    x = -1
    def foo():
        #And I know that it doesn't work with row bellow and without it    
        #global x  
        x += 1
        return x
    return foo

这只是一个样本。我想知道Python中的闭包。

4 个答案:

答案 0 :(得分:5)

Python 3使用PEP 3104nonlocal语句引入了这种范围行为:

>>> def uniqueId ():
        x = -1
        def inner ():
            nonlocal x
            x += 1
            return x
        return inner

>>> f = uniqueId()
>>> f()
0
>>> f()
1
>>> f()
2

除此之外,在以前的版本中,闭包确实存在,但您只有只读访问权限。因此,更改x将无效。但是你可以做的是使用一个可变对象,比如一个列表,并改变它:

>>> def uniqueId ():
        x = [-1]
        def inner ():
            x[0] += 1
            return x[0]
        return inner

>>> f = uniqueId()
>>> f()
0
>>> f()
1

由于您可以使任何类型的对象可调用,您还可以通过定义具有__call__方法的自己的类型来做更多花哨的事情:

>>> class UniqueId:
        def __init__ (self):
            self.x = -1
        def __call__ (self):
            self.x += 1
            return self.x

>>> f = UniqueId()
>>> f()
0
>>> f()
1

答案 1 :(得分:3)

如果你想要的只是一个唯一的ID,只需使用以下内容:

def uniqueID():
    x = 0
    while True:
        yield x  
        x += 1

id = next(uniqueID)

如果您愿意,可以使用闭包(poke mentions in his answer)重写此内容:

def getUniqueIDfunction():
    x = -1
    def uniqueID():
        nonlocal x
        x += 1
        return x
    return uniqueID

uniqueID = getUniqueIDfunction()
id = uniqueID()

这有一点需要注意,它只适用于Python 3+。对于Python 2,您可以通过将值x附加到类来模拟此行为。

答案 2 :(得分:2)

这样可行,但并不能完全符合您的要求:

def getUniqueIDfunction():
    x = -1
    def foo(x=x):
        x += 1
        return x
    return foo
f() # returns 0
f() # returns 0 again!

因为整数数据类型是不可变的。相反,如果您使用可变数据类型:

def counter():
    x = [0]
    def enc():
        x[0] = x[0] + 1
        return x[0]
    return enc
f = counter()
f() # returns 1
f() # returns 2
f() # returns 3

另一个更复杂的例子来自我自己的用法:

def enumerate_dupes_in_column():
    '''
    provides a dict for counting in the namespace and a function for the
    operation, thus avoiding global variable naming
    '''
    countdict = {}
    def countfunction(arg):
        countdict[arg] = countdict.get(arg, 0) + 1
        if countdict[arg] == 1: 
            return arg
        else: 
            return arg + ', ' + str(countdict[arg])
    return countfunction

f = enumerate_dupes_in_column()
f('foo') # returns foo
f('bar') # returns bar
f('foo') # returns foo, 2

答案 3 :(得分:1)

如果要显式指定某些内容是闭包变量,而不是本地或全局变量,则使用nonlocal语句。所以:

def foo():
    nonlocal x  
    x += 1
    return x

在Python 2.x中,没有nonlocal语句。您最好的选择是升级到该语言的现代版本。如果你不能这样做,有很多方法可以伪造它,这在常见问题解答和PEP 3104(介绍nonlocal)中有所解释。