#! /usr/bin/python3 import time from timeit import * from functools import wraps # See Python Cookbook, Chapter 9, metaprogramming def bold(func): def g(*args): print ("", end="") func(*args) print ("", end="") return g def italic(func): def g(*args): print ("", end="") func(*args) print ("", end="") return g @bold @italic def test(s): 'this is test' print (str(s), end="") def debug(f): def g(*args): print ("Calling: {} with args: {}".format(f.__name__, args)) val = f(*args) print ("Exiting: {} with value: {}".format(f.__name__, val)) return val return g @debug def add2(a): return a + 2 ## from Python Cookbook, chapter 9.1 def timethis(func): ''' Decorator that reports the execution time. ''' @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end-start) return result return wrapper @timethis def countdown(n): ''' Counts down ''' while n > 0: n -= 1 ## unwrapping: # oldcountdown = countdown.__wrapped__ def trace(f): f.indent = 0 def g(x): print('| ' * f.indent + '|--', f.__name__, x) f.indent += 1 value = f(x) print('| ' * f.indent + '|--', 'return', repr(value)) f.indent -= 1 return value return g @trace def fib(n): if n == 0: return 1 elif n == 1: return 1 else: return fib(n - 1) + fib(n - 2) ##------------------- ## memoized fib ##------------------- def memofib(n): table = [False for x in range(n+1)] return memofibaux(n,table) def memofibaux(n, table): if table[n]: return table[n] if n <= 1: return 1 else: table[n] = memofibaux(n-1, table) + memofibaux(n-2, table) return table[n] ##------------------- ## another memoized fib ##------------------- def memoized(f): """Decorator that caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned, and not re-evaluated. """ cache = {} @wraps(f) def wrapped(*args): try: result = cache[args] except KeyError: result = cache[args] = f(*args) return result return wrapped @trace @memoized def mfib(n): if n <= 0: return 1 if n == 1: return 1 else: return mfib(n-1) + mfib(n-2) ## ==>> memoized(mfib)(n) ## better way with constant space. Do not need n-entry table def fibbetter(n): return fibbetteraux(n, 1, 1) ## Note: this is tail recursive. Reflected in trace output. def fibbetteraux(n, x, y): if n <= 0: return x else: return fibbetteraux(n-1,y, x+y) ## should turn off trace first def doit(): f1 = timeit('fib(10)', setup = 'from __main__ import fib', number=1) f2 = timeit('memofib(10)', setup = 'from __main__ import memofib', number=1) f3 = timeit('mfib(10)', setup = 'from __main__ import mfib', number=1) f4 = timeit('fibbetter(10)', setup = 'from __main__ import fibbetter', number=1) return f1, f2, f3, f4 ''' Note about @wraps See Python Cookbook, chapter 9.2 from functools import wraps ''' def without_wraps(func): def __wrapper(*args, **kwargs): return func(*args, **kwargs) return __wrapper def with_wraps(func): @wraps(func) def __wrapper(*args, **kwargs): return func(*args, **kwargs) return __wrapper @without_wraps def my_func_a(): """Here is my_func_a doc string text.""" pass @with_wraps def my_func_b(): """Here is my_func_b doc string text.""" pass print (my_func_a.__doc__) print (my_func_a.__name__) print (my_func_a.__module__) ##print (my_func_a.__wrapped__) print (my_func_b.__doc__) print (my_func_b.__name__) print (my_func_b.__module__) print (my_func_b.__wrapped__)