Would you like to add an extra set of wrappers around a series of function of method calls - perhaps to add a profiler or logger to your application? These are examples of the things you can do with Python's decorators, introduced (with the syntax I'll show you below) in Python 2.4.
Here's a class of object that I've defined called "User". The only difference from regular code is the
@logger line that's been added before each definition to ask for it to be wrapped in (decorated) with something called logger.
class User:
@logger
def __init__(self,username,email):
self.uname = username
self.email = email
@logger
def getemail(self):
return self.email
@logger
def getname(self):
return self.uname
And here's the code of a test application that uses that class:
team = []
team.append(User("Lisa","lisa@wellho.net"))
team.append(User("Leah","leah@wellho.net"))
team.append(User("Christine","christine@wellho.net"))
for member in team:
print "Team member",member.getname(),\
"has email",member.getemail()
The results when I run (with the correct version of Python, of course!) are very much as you would expect:
Dorothy:~/ grahamellis$ /usr/local/bin/python dec01 (Python 2.5)
Team member Lisa has email lisa@wellho.net
Team member Leah has email leah@wellho.net
Team member Christine has email christine@wellho.net
Dorothy:~/ grahamellis$ python dec01 (Python 2.3)
File "dec01", line 21
@logger
^
SyntaxError: invalid syntax
Dorothy:~/ grahamellis$
However ... there's an extra log file that was generated by the code in the decorator -
***__init__ <function __init__ at 0x650f0>
(<__main__.User instance at 0x66620>, 'Lisa', 'lisa@wellho.net'){}
***__init__ <function __init__ at 0x650f0>
(<__main__.User instance at 0x66670>, 'Leah', 'leah@wellho.net'){}
***__init__ <function __init__ at 0x650f0>
(<__main__.User instance at 0x66698>, 'Christine', 'christine@wellho.net'){}
***getname <function getname at 0x651f0>
(<__main__.User instance at 0x66620>,){}
***getemail <function getemail at 0x65170>
(<__main__.User instance at 0x66620>,){}
***getname <function getname at 0x651f0>
(<__main__.User instance at 0x66670>,){}
***getemail <function getemail at 0x65170>
(<__main__.User instance at 0x66670>,){}
***getname <function getname at 0x651f0>
(<__main__.User instance at 0x66698>,){}
***getemail <function getemail at 0x65170>
(<__main__.User instance at 0x66698>,){}
The final piece of the jigsaw is the decorator itself - loaded in the top of the test source file in my example (full source code listing
here but more often in its own separate module.
def logger(f, name=None):
# if logger.fhwr isn't defined and open ...
try:
if logger.fhwr:
pass
except:
# ... open it
logger.fhwr = open("log.txt","w")
if name is None:
name = f.func_name
def wrapped(*args, **kwargs):
logger.fhwr.write("***"+name+" "+str(f)+"\n"\
+str(args)+str(kwargs)+"\n\n")
result = f(*args, **kwargs)
return result
wrapped.__doc__ = f.__doc__
return wrapped
That's not a piece of "first day Python", with exceptions to check on the existance of a variable, names containing code blocks, and references to a number of interbal variables - but it will provide you with an excellent springboard from which you can start using decorators.
Have you even said to yourself "I wish I could put all my function calls through this extra filter ....? Well - now you can - it's a Python decorator! (written 2007-04-15)
Associated topics are indexed as below, or enter http://melksh.am/nnnn for individual articles
Y212 - Python - Code testing, patterns, profiles and optimisation. [4716] Profiling your Python program - (2016-11-01)
[4618] Pytest - second example beyond hello world - (2016-01-08)
[4617] Pytest - starting example - (2016-01-07)
[4542] The principle of mocking - and the Python Mock package - (2015-10-17)
[4540] Unittest of a Flask application including forms - (2015-10-15)
[4538] Flask and unittest - hello web app test world - (2015-10-15)
[4470] Testing in Python 3 - unittest, doctest and __name__ == __main__ too. - (2015-04-21)
[4446] Combining tests into suites, and suites into bigger suites - Python and unittest - (2015-03-01)
[4344] Python base and inherited classes, test harness and unit testing - new examples - (2014-12-07)
[4326] Learning to program - comments, documentation and test code - (2014-11-22)
[4090] Test Driven Development in Python - Customer Comes First - (2013-05-16)
[3658] Using Make for a distribution - (2012-03-17)
[3478] Testing your Python classes with the unittest package - how to - (2011-10-14)
[3464] Passing optional and named parameters to python methods - (2011-10-04)
[3442] A demonstration of how many Python facilities work together - (2011-09-16)
[3441] Pressing ^C in a Python program. Also Progress Bar. - (2011-09-15)
[2616] Defining a static method - Java, Python and Ruby - (2010-02-01)
[2123] Using Python with OpenOffice - (2009-04-09)
[1555] Advanced Python, Perl, PHP and Tcl training courses / classes - (2008-02-25)
[1146] __new__ v __init__ - python constructor alternatives? - (2007-04-14)
[1140] Python GTK - Widget, Packing, Event and Feedback example - (2007-04-09)
[235] Preparation for a day's work - (2005-03-04)
Some other Articles
Course, right place, right timeGordon Dodge, R.I.P.Helsinki - what comes naturallyTurning objects into something you can store - Pickling (Python)Python decorators - wrapping a method call in extra codeA picture (mostly in words) of HelsinkiUsing a list of keys and a list of values to make a dictionary in Python - zipPython dictionary for quick look upsA course in Helsinki