Decorators
From Python 2.4 the language was extended with a new syntax that allows the convenient
wrapping of one function with another; to modify a function so that its output passes
directly to another for further processing. Doing this was actually possible before the new
syntax was invented, but the specification of how one function wrapped another had to
come at the end, after the definition of the function. This meant that it was not always
obvious at first glance that a function had been wrapped, and consequently that its output
might be modified. Consider, for example, the following trivial example function to do a
simple mathematical operation:
def getSumSquares(n):
result = sum([i*i for i in range(n)])
return result
Next we consider a wrapping function addEmphasis that is designed to work in a
variety of situations (including for the function defined above). It takes an inputFunc
function and defines an inner modifyFunc function that runs the first inputFunc but
modifies the result, in this case to make text with added ‘*’ symbols. Note that the main
function passes back only a reference to the inner modifyFunc; it only defines a new
function based on the input one and doesn’t actually run anything.
def addEmphasis(inputFunc):
def modifyFunc(*args):
result = inputFunc(*args)
result = '****%d****' % result
return result
return modifyFunc
The function addEmphasis is designed so that an input function can be redefined to
include the modification. For example, getSumSquares can be changed from a numeric
output to produce text with asterisk symbols:
print(getSumSquares(127)) # Gives: 674751
getSumSquares = addEmphasis(getSumSquares) # Redefine
print(getSumSquares(127)) # Gives: ****674751****
With the decorator syntax, rather than redefining a function at the end, we ‘decorate’ it
at the start with a wrapper function specification using the ‘@’ symbol:
@addEmphasis
def getSumSquares(n):
result = sum([i*i for i in range(n)])
return result
This ‘@’ syntax can be used to add any number of decorator functions. Take, for
example, another general wrapping function, which in this case times how long something
takes to run:
def usingTimer(function):
from time import time
def timer(*args, **kw):
start = time()
output = function(*args, **kw)
end = time()
print('Function call took %f seconds' % (end-start))
return output
return timer
To add this wrapping to the specification we only need to add one line to the start:
@usingTimer
@addEmphasis
def getSumSquares(n):
result = sum([i*i for i in range(n)])
return result
print(getSumSquares(127))
And so when the same test is applied again the result is further modified to print out the
timing information.
Function call took 0.000074 seconds
****674751****
1
If a variable is defined outside a function then code that changes it is not ‘thread safe’,
without extra code to manage that.
Do'stlaringiz bilan baham: |