| talk given at: | EuroPython 2006 |
|---|---|
| by: | Michele Simionato |
| date: | 2006-07-04 |
An introduction to Python 2.4 decorators.
# example of built-in decorator
@classmethod
def makeinstance(cls):
return cls()
There already libraries out there that provide custom decorators. I will show two examples of many.
import cherrypy as cp
class Root(object):
@cp.expose
def index(self):
return 'This is the index page'
if __name__ == '__main__':
cp.root = Root()
cp.server.start()
def expose(func):
"Expose a function"
func.exposed = True
return func
@expose
def foo(): pass
>>> foo.exposed True
In other words
@decor def foo(): pass
is (essentially) a shortcut for
def foo(): pass foo = decor(foo)
all Turing-complete languages are equivalent up to syntactic sugar
=>
syntactic sugar is the most important thing ever!!
;-)
*Decorators changed the way we think about functions*
from __future__ import with_statement
from contextlib import contextmanager
@contextmanager
def cursor(conn):
curs = conn.cursor()
try:
yield curs
except:
conn.rollback(); raise
finally:
curs.close()
conn.commit()
import psycopg
from contextlib import closing
with closing(psycopg.connect('')) as conn:
with cursor(conn) as c:
c.execute('create table example (name char(3))')
c.execute("insert into example values ('foo')")
c.execute("insert into example values ('bar')")
c.execute('select * from example')
print c.fetchall()
Naive implementation:
def traced(func):
def newfunc(*args,**kw):
print 'Calling %s with arguments %s, %s' % (
func.__name__, args, kw)
return func(*args, **kw)
return newfunc
@traced
def square(x):
return x*x
>>> square(2)
Calling square with arguments (2,), {}
4
However the naive implementation breaks introspection:
>>> help(square) Help on function newfunction in module __main__: newfunction(*args, **kw)
>>> def traced(func): ... def newfunc(*args,**kw): ... print 'Calling %s with arguments %s, %s' % ( ... func.__name__, args, kw) ... return func(*args, **kw) ... newfunc.__name__ = func.__name__ ... newfunc.__doc__ = func.__doc__ ... newfunc.__module__ = func.__module__ ... newfunc.__dict__ = func.__dict__ ... return newfunc
but the signature is still broken :-(
from decorator import decorator
@decorator
def traced(func, *args, **kw):
print 'Calling %s with arguments %s, %s' % (
func.__name__, args, kw)
return func(*args, **kw)
@traced
def square(x):
return x*x
>>> square(2)
Calling square with arguments (2,), {}
4
>>> help(square)
Help on function square in module __main__:
square(x)
>>> isinstance(traced, decorator)
True
timing:
@time def mycomputation(): pass
logging:
@log def myprocedure(): pass
caching:
@cached def mylongcomputation(): pass
access control:
@admin def showpage(): pass @user def showpage(): pass
remove boilerplate in locking, transactions, ...
mind boggling exercises:
@tail_recursive def fact(n, acc=1): return fact(n-1, acc*n)
etc. etc.