简体   繁体   中英

Closure in python. Can I make closure on local context of function?

In javascript I can write function with closure like this

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

And then use it

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

Can I perform the same in Python (if it depends with different version let me know) ?

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

It's just a sample. I want know about closure in Python.

Python 3 introduced this kind of scoping behavior with PEP 3104 and the nonlocal statement:

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

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

Other than that, in previous versions, closures do exist, but you have only a read-only access. So changing x will not work. What you can do however is use a mutable object, like a list, and change that one:

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

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

As you can make any kind of object callable, you can also do something more fancy by defining your own type that has a __call__ method:

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

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

If all you want is a unique ID, just use the following:

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

id = next(uniqueID)

You can rewrite this with a closure (as poke mentions in his answer ), if you wish to:

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

uniqueID = getUniqueIDfunction()
id = uniqueID()

This has the caveat that it only works in Python 3+. For Python 2, you can simulate this behavior by attaching the value x to a class.

This works, but doesn't do exactly what you want:

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

Because the integer datatype is immutable. If instead you use a mutable datatype:

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

Another more complicated example from my own usage:

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

If you want to explicitly specify that something is a closure variable, not a local or global, you use the nonlocal statement. So:

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

In Python 2.x, there is no nonlocal statement. Your best bet is to upgrade to a modern version of the language. If you can't do that, there are ways to fake it, which are explained in the FAQ, and in PEP 3104 (which introduced nonlocal ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM