Skip to content

Instantly share code, notes, and snippets.

@hunan-rostomyan
Last active August 26, 2016 23:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hunan-rostomyan/7ed95562c0e961ead8dc to your computer and use it in GitHub Desktop.
Save hunan-rostomyan/7ed95562c0e961ead8dc to your computer and use it in GitHub Desktop.
Closures: Javascript vs Python
/*
Calling Counter with (a possibly `undefined`) `init`
returns a function that closes over `counter` and
increments it every time it's called.
*/
function Counter(init) {
var counter = init || 1;
return function() {
var current = counter;
counter += 1;
return current;
};
}
var c = Counter();
c() //=> 1
c() //=> 2
c() //=> 3
"""
Instantiating Counter with (a possibly `None`) `init`
we obtain a callable object (effectively, a function) with
instance variable `counter` initialized to `init` or 0 that
gets incremented every time the 'function' it called.
"""
class Counter:
def __init__(self, init=0):
self.counter = init
def __call__(self):
self.counter += 1
return self.counter
c = Counter()
c() #=> 1
c() #=> 2
c() #=> 3
"""
I was playing around with a simple decorator for tracking
function calls. What follows are two failed attempts and
an interesting workaround suggested by Eric D. Wang.
"""
"""
Failed attempt #1
"""
def track_calls(fn):
counter = 0
def _fn(*args, **kwargs):
result = fn(*args, **kwargs)
counter += 1
print 'That was call #{}'.format(counter)
return result
return _fn
@track_calls
def add(a, b):
return a + b
add(2, 3) # UnboundLocalError: local variable 'counter'
# referenced before assignment
"""
Failed attempt #2
This seemed to work, but only because I had declared `counter`
globally. Thanks to Eric D. Wang for pointing this out.
"""
def track_calls(fn):
counter = 0
def _fn(*args, **kwargs):
result = fn(*args, **kwargs)
global counter
counter += 1
print 'That was call #{}'.format(counter)
return result
return _fn
@track_calls
def add(a, b):
return a + b
add(2, 3) # NameError: global name 'counter' is not defined
"""
Eric's workaround:
"You can get around it though by keeping a
counter in a mutable object (kinda hacky)"
"""
def track_calls(fn):
counter = [0]
def _fn(*args, **kwargs):
result = fn(*args, **kwargs)
counter[0] += 1
print 'That was call #{}'.format(counter[0])
return result
return _fn
@track_calls
def add(a, b):
return a + b
add(2, 3) #=> 5 (side effect: That was call #1)
add(2, 3) #=> 5 (side effect: That was call #2)
add(2, 3) #=> 5 (side effect: That was call #2)
"""
Another option is to use the `nonlocal` keyword available
since version 3. Here is the basic idea.
"""
def counter():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
c = counter()
c() #=> 1
c() #=> 2
c() #=> 3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment