Lecture#4
.pdfDecorators rationale
Add functionality without changing the code
Examples:
logging, timing, tests
Decorators rationale
def wrapper(f):
def inner(*args):
print “Gonna call %s” % f.func_name
f(*args)
return inner def func(*args): do_something
func = wrapper(func) #additional functionality
Decorators rationale
def wrapper(f):
def inner(*args):
print “Gonna call %s” % f.func_name
f(*args)
return inner
@wrapper
def func(*args):
do_something
Decorators rationale
And now magic happens: wrapper can be used to decorate any function. Code reuse, good design - PROFIT!
...
@wrapper
def f1(*args):
do_something
@wrapper
def f2(*args):
do_something_else
...
Decorators parametrized
What if your decorator depends on some value? That’s ok.
def dec(a):
def wrapper(f): ← This is actual decorator def inner(*args):
print “Func is %s, param is %s” % (f.func_name,str(a))
f(*args)
return inner
return wrapper
@dec(‘Some string parameter’)
def func(*args):
...
Decorators leveraging __call__
class MyDecorator(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
print “Starting %s call” %s self.f.func_name
self.f(*args, **kwargs)
print “Finished %s” %s self.f.func_name
@MyDecorator
def func(*args, **kwargs):
...
Cool. Really.
Metaprogramming code that creates code
class MyClass(object):
def __init__(self, data):
self.data = data ← Something interesting
...
...
Classes and instances can be extended at runtime
Metaprogramming code that creates code
>>>MyClass.new_method = lambda self: self.data * 2
>>>m = MyClass(5.)
>>>m.new_method()
10.
Classes and instances can be extended at runtime
Metaprogramming rationale
class IsothermalIdealGas(object):
pressure = 1.
temp = 1.
volume = 1. quant = 1.
Not cool. Really. PV = nT? Never mind.
(forget about R, this is computer science class)
Developer of IsothermalIdealGas never heard about equation of state. Let’s patch it.
Metaprogramming templating: jinja2
from jinja2 import Template
tpl = Template(“””def custom_setattr(self, name, value): if name == 'P':
object.__setattr__(self, name, value)
object.__setattr__(self, 'V', {{V_eq}})
elif name == 'V':
object.__setattr__(self, name, value)
object.__setattr__(self, 'P', {{P_eq}})”””)
v_eq = Template(“self.{{T}}*self.{{Q}} / self.{{V}}**{{k}}”)
p_eq = Template(“(self.{{T}}*self.{{Q}} / self.{{P}})**(1./
{{k}})”)
keys = {‘T’:’T’, ‘P’:’P’, ‘V’:’V’, ‘Q’:’Q’, ‘k’:2}
source = tpl.render(V_eq=v_eq.render(...), P_eq=p_eq.render(...))