A decorator is a function which accepts a function and returns a
new function. Consequently, most decorators include a function
definition and a
return
statement. A common
alternative is to include a class definition as well as a
return
statement. If a class definition is used, it
must define a callable object by including a definition for the
__call__
method.
There are two kinds of decorators, decorators without arguments
and decorators with arguments. In the first case, the operation of the
decorator is very simple. In the second case, the definition of the
decorator is rather obscure. We'll look at the simple decorators first.
We'll defer more complex decorators to the next section.
A simple decorator has the following outline:
def myDecorator( argumentFunction ):
def resultFunction( *args, **keywords ):
enhanced processing
including a call to argumentFunction
resultFunction.__doc__= argumentFunction.__doc__
return resultFunction
In some cases, we may replace the result function definition with
a result class definition to create a callable class.
Here's a simple decorator that we can use for debugging. This will
log function entry, exit and exceptions.
Example 26.2. trace.py
def trace( aFunc ):
"""Trace entry, exit and exceptions."""
def loggedFunc( *args, **kw ):
print "enter", aFunc.__name__
try:
result= aFunc( *args, **kw )
except Exception, e:
print "exception", aFunc.__name__, e
raise
print "exit", aFunc.__name__
return result
loggedFunc.__name__= aFunc.__name__
loggedFunc.__doc__= aFunc.__doc__
return loggedFunc
|
The result function, loggedFunc , is
built when the decorator executes. This creates a fresh, new
function for each use of the decorator.
|
|
Within the result function, we evaluate the original
function. Note that we simply pass the argument values from the
evaluation of the result function to the original
function.
|
|
We move the original function's docstring and name to the
result function. This assures us that the result function looks
like the original function.
|
Here's a class which uses our
@trace
decorator.
Example 26.3. trace_user.py
class MyClass( object ):
@trace
def __init__( self, someValue ):
"""Create a MyClass instance."""
self.value= someValue
@trace
def doSomething( self, anotherValue ):
"""Update a value."""
self.value += anotherValue
Our class definition includes two traced function definitions.
Here's an example of using this class with the traced functions. When we
evaulate one of the traced methods it logs the entry and exit events for
us. Additionally, our decorated function usees the original method
function of the class to do the real work.
>>>
mc= MyClass( 23 )
enter __init__
exit __init__
>>>
mc.doSomething( 15 )
enter doSomething
exit doSomething
>>>
mc.value
38