Decorators are a way to do aspect oriented programming in Python. Decorators were added in Python 2.4 and many libraries make extensive use of them.
In the simplest form
@dec
def foo():
pass
is equivalent to
def foo():
pass
foo = dec(foo)
For the above you can see that a decorator is a function that takes a function as an argument and returns a function.
Since their introduction in Python 2.4, decorators are everywhere. Including some in the standard library: property, staticmethod and others. Many libraries also use decorators: Celery @task, Flask @app.route and many more.
Decorators can be nested
@logged
@metrics.ncalls
@retry(times=3)
def whos_on_first():
return 'who'
There are also class decorators (added in Python 2.6). You will probably won't need them (unless you wrote metaclasses before).
@plugin
class FunnyPlugin(object):
pass
In [19]:
from functools import wraps
def greeter(fn):
@wraps(fn)
def wrapper(*args, **kw):
print('Hello {}'.format(fn.__name__))
try:
return fn(*args, **kw)
finally:
print('Bye {}'.format(fn.__name__))
return wrapper
In [21]:
@greeter
def add(x, y):
'''Adds x to y'''
return x / y
print(add(1, 2))
print(add.__name__)
In [24]:
from time import time
def timed(fn):
@wraps(fn)
def wrapper(*args, **kw):
start = time()
try:
return fn(*args, **kw)
finally:
duration = time() - start
print('{} took ({:.2f}sec)'.format(fn.__name__, duration))
return wrapper
In [25]:
from time import sleep
@timed
def mul(x, y):
'''Multiply x with y'''
sleep(0.2)
return x * y
mul(8, 4)
# return 32 and print timing info
Out[25]:
In [ ]:
# Your code goes here
Write a decorator that caches the results of the invoked function. Computing every value only once. (See lru_cache in Python > 3.2).
In [4]:
def cached(fn):
cache = {} # Can be accessed from wrapper
def wrapper(*args, **kw):
# You code goes here
# Remember that dict keys can be any immutable object and `args` is a tuple)
# For simplicity, you can ignore **kw
pass
return wrapper
In [35]:
@cached
def fib(n):
'''Return the n'th fibonacci number'''
print('fib({})'.format(n))
if n < 2:
return 1
return fib(n-1) + fib(n-2)
fib(5)
Out[35]:
In [ ]:
# Your code goes here
In [ ]:
@mulby(7)
def inc(x):
'''Add 1 to x'''
return x + 1
inc(2) # 21
In [38]:
class A(object):
def __init__(self, x):
self._x = x
@property
def x(self):
return self._x
@staticmethod
def y():
return 8
a = A(7)
print(a.x)
print(A.y())
property makes a function looks like an ordinary attribute. It let's you change your mind and have more controlled access to attribute (say locking) without any change to the client.
In [5]:
class Parrot(object):
def __init__(self, voltage):
self._voltage = voltage
@property
def voltage(self):
return self._voltage
p = Parrot(10)
print(p.voltage) # Note: Not a function call
In [ ]: