A decorator factory

Let's simplify this example now, going back to a single decorator: max_result. I want to make it so that I can decorate different functions with different thresholds, as I don't want to write one decorator for each threshold. Let's amend max_result so that it allows us to decorate functions specifying the threshold dynamically:

# decorators/decorators.factory.py
from functools import wraps

def max_result(threshold):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
if result > threshold:
print(
'Result is too big ({0}). Max allowed is {1}.'
.format(result, threshold))
return result
return wrapper
return decorator

@max_result(75)
def cube(n):
return n ** 3

print(cube(5))

The preceding code shows you how to write a decorator factory. If you recall, decorating a function with a decorator that takes arguments is the same as writing func = decorator(argA, argB)(func), so when we decorate cube with max_result(75), we're doing cube = max_result(75)(cube).

Let's go through what happens, step by step. When we call max_result(75), we enter its body. A decorator function is defined inside, which takes a function as its only argument. Inside that function, the usual decorator trick is performed. We define wrapper, inside of which we check the result of the original function's call. The beauty of this approach is that from the innermost level, we can still refer to as both func and threshold, which allows us to set the threshold dynamically.

wrapper returns result, decorator returns wrapper, and max_result returns decorator. This means that our cube = max_result(75)(cube) call actually becomes cube = decorator(cube). Not just any decorator though, but one for which threshold has a value of 75. This is achieved by a mechanism called closure, which is outside of the scope of this chapter but still very interesting, so I mentioned it for you to do some research on it.

Running the last example produces the following result:

$ python decorators.factory.py
Result is too big (125). Max allowed is 75.
125

The preceding code allows me to use the max_result decorator with different thresholds at my own will, like this:

# decorators/decorators.factory.py
@max_result(75)
def cube(n):
return n ** 3

@max_result(100)
def square(n):
return n ** 2

@max_result(1000)
def multiply(a, b):
return a * b

Note that every decoration uses a different threshold value.

Decorators are very popular in Python. They are used quite often and they simplify (and beautify, I dare say) the code a lot.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset