#! /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))
print()
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__)
'''