Functional Python Programming

(Wang) #1

Decorator Design Techniques


We could also create the null-aware rounding function using the following:


nround4= nullable(lambda x: round(x,4))


This has the same effect, at some cost in clarity.


We can use this round4() function to create a better test case for our nlog() function
as follows:





some_data = [10, 100, None, 50, 60]








scaled = map(nlog, some_data)








[nround4(v) for v in scaled]





[2.3026, 4.6052, None, 3.912, 4.0943]


This result will be independent of any platform considerations.


This decorator makes an assumption that the decorated function is unary. We would
need to revisit this design to create a more general-purpose null-aware decorator that
works with arbitrary collections of arguments.


In Chapter 14, The PyMonad Library, we'll look at an alternative approach to this
problem of tolerating the None values. The PyMonad library defines a Maybe class of
objects which may have a proper value or may be the None value.


Using functool's update_wrapper() functions


The @wraps decorator applies the update_wrapper() function to preserve a few
attributes of a wrapped function. In general, this does everything we need by
default. This function copies a specific list of attributes from the original function to
the resulting function created by a decorator. What's the specific list of attributes? It's
defined by a module global.


The update_wrapper() function relies on a module global variable to determine what
attributes to preserve. The WRAPPER_ASSIGNMENTS variable defines the attributes that
are copied by default. The default value is this list of attributes to copy:


('module', 'name', 'qualname', 'doc',
'annotations')


It's difficult to make meaningful modifications to this list. In order to copy additional
attributes, we have to assure that our functions are defined with these additional
attributes. This is challenging, since the internals of the def statement aren't open to
simple modification or change.

Free download pdf