Online Book Reader

Home Category

Learning Python - Mark Lutz [558]

By Root 1441 0
such arguments are not listed in the function’s definition, there’s no way to map a name given to the decorator back to an expected relative position.

In other words, as it is the code supports testing arbitrary keyword arguments by name, but not arbitrary positionals that are unnamed and hence have no set position in the function’s argument signature.

In principle, we could extend the decorator’s interface to support *args in the decorated function, too, for the rare cases where this might be useful (e.g., a special argument name with a test to apply to all arguments in the wrapper’s *pargs beyond the length of the expected arguments list). Since we’ve already exhausted the space allocation for this example, though, if you care about such improvements you’ve officially crossed over into the realm of suggested exercises.

Decorator Arguments Versus Function Annotations

Interestingly, the function annotation feature introduced in Python 3.0 could provide an alternative to the decorator arguments used by our example to specify range tests. As we learned in Chapter 19, annotations allow us to associate expressions with arguments and return values, by coding them in the def header line itself; Python collects annotations in a dictionary and attaches it to the annotated function.

We could use this in our example to code range limits in the header line, instead of in decorator arguments. We would still need a function decorator to wrap the function in order to intercept later calls, but we would essentially trade decorator argument syntax:

@rangetest(a=(1, 5), c=(0.0, 1.0))

def func(a, b, c): # func = rangetest(...)(func)

print(a + b + c)

for annotation syntax like this:

@rangetest

def func(a:(1, 5), b, c:(0.0, 1.0)):

print(a + b + c)

That is, the range constraints would be moved into the function itself, instead of being coded externally. The following script illustrates the structure of the resulting decorators under both schemes, in incomplete skeleton code. The decorator arguments code pattern is that of our complete solution shown earlier; the annotation alternative requires one less level of nesting, because it doesn’t need to retain decorator arguments:

# Using decorator arguments

def rangetest(**argchecks):

def onDecorator(func):

def onCall(*pargs, **kargs):

print(argchecks)

for check in argchecks: pass # Add validation code here

return func(*pargs, **kargs)

return onCall

return onDecorator

@rangetest(a=(1, 5), c=(0.0, 1.0))

def func(a, b, c): # func = rangetest(...)(func)

print(a + b + c)

func(1, 2, c=3) # Runs onCall, argchecks in scope

# Using function annotations

def rangetest(func):

def onCall(*pargs, **kargs):

argchecks = func.__annotations__

print(argchecks)

for check in argchecks: pass # Add validation code here

return func(*pargs, **kargs)

return onCall

@rangetest

def func(a:(1, 5), b, c:(0.0, 1.0)): # func = rangetest(func)

print(a + b + c)

func(1, 2, c=3) # Runs onCall, annotations on func

When run, both schemes have access to the same validation test information, but in different forms—the decorator argument version’s information is retained in an argument in an enclosing scope, and the annotation version’s information is retained in an attribute of the function itself:

{'a': (1, 5), 'c': (0.0, 1.0)}

6

{'a': (1, 5), 'c': (0.0, 1.0)}

6

I’ll leave fleshing out the rest of the annotation-based version as a suggested exercise; its code would be identical to that of our complete solution shown earlier, because range-test information is simply on the function instead of in an enclosing scope. Really, all this buys us is a different user interface for our tool—it will still need to match argument names against expected argument names to obtain relative positions as before.

In fact, using annotation instead of decorator arguments in this example actually limits its utility. For one thing, annotation only works under Python 3.0, so 2.6 is no longer supported; function decorators with arguments, on the other hand, work in both versions.

Return Main Page Previous Page Next Page

®Online Book Reader