Reputation: 3933
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.
Upvotes: 2
Views: 241
Reputation: 395633
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
Upvotes: 2
Reputation: 10499
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.
Upvotes: 2
Reputation: 388163
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
Upvotes: 5
Reputation: 365975
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
).
Upvotes: 1