Last active
December 19, 2015 23:19
-
-
Save sahands/6034061 to your computer and use it in GitHub Desktop.
A Study of Python's More Advanced Features: Part II - Closures, Decorators and functools
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
from functools import wraps | |
def cached(timeout, logged=False): | |
"""Decorator to cache the result of a function call. | |
Cache expires after timeout seconds. | |
""" | |
def decorator(func): | |
if logged: | |
print "-- Initializing cache for", func.__name__ | |
cache = {} | |
@wraps(func) | |
def decorated_function(*args, **kwargs): | |
if logged: | |
print "-- Called function", func.__name__ | |
key = repr(args) + repr(kwargs) | |
result = None | |
if key in cache: | |
if logged: | |
print "-- Cache hit for", func.__name__, key | |
(cache_hit, expiry) = cache[key] | |
if time.time() - expiry < timeout: | |
result = cache_hit | |
elif logged: | |
print "-- Cache expired for", func.__name__, key | |
elif logged: | |
print "-- Cache miss for", func.__name__, key | |
# No cache hit, or expired | |
if result == None: | |
result = func(*args, **kwargs) | |
cache[key] = (result, time.time()) | |
return result | |
return decorated_function | |
return decorator | |
@cached(10, True) | |
def fib(n): | |
"""Returns the n'th Fibonacci number.""" | |
if n == 0 or n == 1: | |
return 1 | |
return fib(n - 1) + fib(n - 2) | |
print "%s - %s" % (fib.__name__, fib.__doc__) | |
print "Dumping function's closure:" | |
print "Testing - F(4) = %d" % fib(4) | |
for c in fib.func_closure: | |
print c.cell_contents | |
@cached(2, True) | |
def cached_time(): | |
return time.time() | |
for i in xrange(5): | |
print cached_time() | |
time.sleep(1) | |
# Output: | |
-- Initializing cache for fib | |
fib - Returns the n'th Fibonacci number. | |
Dumping function's closure: | |
-- Called function fib | |
-- Cache miss for fib (4,){} | |
-- Called function fib | |
-- Cache miss for fib (3,){} | |
-- Called function fib | |
-- Cache miss for fib (2,){} | |
-- Called function fib | |
-- Cache miss for fib (1,){} | |
-- Called function fib | |
-- Cache miss for fib (0,){} | |
-- Called function fib | |
-- Cache hit for fib (1,){} | |
-- Called function fib | |
-- Cache hit for fib (2,){} | |
Testing - F(4) = 5 | |
{'(3,){}': (3, 1374300190.333688), '(2,){}': (2, 1374300190.33365), '(4,){}': (5, 1374300190.333725), '(1,){}': (1, 1374300190.333604), '(0,){}': (1, 1374300190.333648)} | |
<function fib at 0x109b331b8> | |
True | |
10 | |
-- Initializing cache for cached_time | |
-- Called function cached_time | |
-- Cache miss for cached_time (){} | |
1374300190.33 | |
-- Called function cached_time | |
-- Cache hit for cached_time (){} | |
1374300190.33 | |
-- Called function cached_time | |
-- Cache hit for cached_time (){} | |
-- Cache expired for cached_time (){} | |
1374300192.33 | |
-- Called function cached_time | |
-- Cache hit for cached_time (){} | |
1374300192.33 | |
-- Called function cached_time | |
-- Cache hit for cached_time (){} | |
-- Cache expired for cached_time (){} | |
1374300194.34 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def dump_closure(f): | |
if hasattr(f, "func_closure") and f.func_closure is not None: | |
print "- Dumping function closure for %s:" % f.__name__ | |
for i, c in enumerate(f.func_closure): | |
print "-- cell %d = %s" % (i, c.cell_contents) | |
else: | |
print " - %s has no closure!" % f.__name__ | |
def return_add_stuff_function(x, z): | |
some_constant = [0] | |
def add_stuff(y): | |
return some_constant + x + y + z | |
return add_stuff | |
z = [3] | |
f = return_add_stuff_function([1], z) | |
print f([2]) | |
dump_closure(f) | |
z.append(4) | |
print f([2]) | |
dump_closure(f) | |
# Output: | |
[0, 1, 2, 3] | |
- Dumping function closure for add_stuff: | |
-- cell 0 = [1] | |
-- cell 1 = [0] | |
-- cell 2 = [3] | |
[0, 1, 2, 3, 4] | |
- Dumping function closure for add_stuff: | |
-- cell 0 = [1] | |
-- cell 1 = [0] | |
-- cell 2 = [3, 4] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def make_dual(relation): | |
@wraps(relation, ['__name__', '__doc__']) | |
def dual(x, y): | |
return relation(y, x) | |
return dual | |
def dual_ordering(cls): | |
"""Class decorator that reverses all the orderings""" | |
for func in ['__lt__', '__gt__', '__ge__', '__le__']: | |
if hasattr(cls, func): | |
setattr(cls, func, make_dual(getattr(cls, func))) | |
return cls | |
@dual_ordering | |
class rs(str): | |
pass | |
x = rs("1") | |
y = rs("2") | |
print x < y | |
print x <= y | |
print x > y | |
print x >= y | |
# Output: | |
False | |
False | |
True | |
True |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def ignore(return_value=None): | |
"""Decorator that makes the functional call be ignored returning return_value immediately.""" | |
def wrapper(func): | |
def decorated_function(*args, **kwargs): | |
return return_value | |
return decorated_function | |
return wrapper | |
@ignore(5) | |
def add(x, y): | |
return x + y | |
@ignore() | |
def print_something(): | |
print "Something!" | |
print add(1, 2) | |
print_something() | |
# Output, notice that "Something" is NOT printed: | |
5 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
from functools import wraps | |
def logged(time_format, name_prefix=""): | |
def decorator(func): | |
if hasattr(func, '_logged_decorator') and func._logged_decorator: | |
return func | |
@wraps(func) | |
def decorated_func(*args, **kwargs): | |
print "Log: '%s' called on %s with arguments: %s, %s" % ( | |
name_prefix + func.__name__, | |
time.strftime(time_format), | |
args, | |
kwargs | |
) | |
return func(*args, **kwargs) | |
decorated_func._logged_decorator = True | |
return decorated_func | |
return decorator | |
def log_method_calls(time_format): | |
def decorator(cls): | |
for o in dir(cls): | |
if o.startswith('__'): | |
continue | |
a = getattr(cls, o) | |
if hasattr(a, '__call__'): | |
decorated_a = logged(time_format, cls.__name__ + ".")(a) | |
setattr(cls, o, decorated_a) | |
return cls | |
return decorator | |
@log_method_calls("%b %d %Y - %H:%M:%S") | |
class A(object): | |
def test1(self): | |
print "test1" | |
@log_method_calls("%b %d %Y - %H:%M:%S") | |
class B(A): | |
def test1(self): | |
super(B, self).test1() | |
print "child test1" | |
def test2(self): | |
print "test2" | |
b = B() | |
b.test1() | |
b.test2() | |
# Output: | |
Log: 'B.test1' called on Jul 20 2013 - 16:38:11 with arguments: (<__main__.B object at 0x10ddeff50>,), {} | |
Log: 'A.test1' called on Jul 20 2013 - 16:38:11 with arguments: (<__main__.B object at 0x10ddeff50>,), {} | |
test1 | |
child test1 | |
Log: 'B.test2' called on Jul 20 2013 - 16:38:11 with arguments: (<__main__.B object at 0x10ddeff50>,), {} | |
test2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
def logged(time_format): | |
def decorator(func): | |
def decorated_func(*args, **kwargs): | |
print "Log: '%s' called on %s with arguments: %s, %s" % ( | |
func.__name__, | |
time.strftime(time_format), | |
args, | |
kwargs | |
) | |
return func(*args, **kwargs) | |
decorated_func.__name__ = func.__name__ | |
return decorated_func | |
return decorator | |
@logged("%b %d %Y - %H:%M:%S") | |
def add(x, y): | |
return x + y | |
print add(1, 2) | |
# Output: | |
Log: 'add' called on Jul 19 2013 - 21:47:08 with arguments: (1, 2), {} | |
3 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment