Online Book Reader

Home Category

Learning Python - Mark Lutz [438]

By Root 1397 0
today, for example, may be coded with decorator syntax like this:

class C:

@staticmethod # Decoration syntax

def meth():

...

Internally, this syntax has the same effect as the following (passing the function through the decorator and assigning the result back to the original name):

class C:

def meth():

...

meth = staticmethod(meth) # Rebind name

Decoration rebinds the method name to the decorator’s result. The net effect is that calling the method function’s name later actually triggers the result of its staticmethod decorator first. Because a decorator can return any sort of object, this allows the decorator to insert a layer of logic to be run on every call. The decorator function is free to return either the original function itself, or a new object that saves the original function passed to the decorator to be invoked indirectly after the extra logic layer runs.

With this addition, here’s a better way to code our static method example from the prior section in either Python 2.6 or 3.0 (the classmethod decorator is used the same way):

class Spam:

numInstances = 0

def __init__(self):

Spam.numInstances = Spam.numInstances + 1

@staticmethod

def printNumInstances():

print("Number of instances created: ", Spam.numInstances)

a = Spam()

b = Spam()

c = Spam()

Spam.printNumInstances() # Calls from both classes and instances work now!

a.printNumInstances() # Both print "Number of instances created: 3"

Keep in mind that staticmethod is still a built-in function; it may be used in decoration syntax, just because it takes a function as argument and returns a callable. In fact, any such function can be used in this way—even user-defined functions we code ourselves, as the next section explains.

A First Function Decorator Example

Although Python provides a handful of built-in functions that can be used as decorators, we can also write custom decorators of our own. Because of their wide utility, we’re going to devote an entire chapter to coding decorators in the next part of this book. As a quick example, though, let’s look at a simple user-defined decorator at work.

Recall from Chapter 29 that the __call__ operator overloading method implements a function-call interface for class instances. The following code uses this to define a class that saves the decorated function in the instance and catches calls to the original name. Because this is a class, it also has state information (a counter of calls made):

class tracer:

def __init__(self, func):

self.calls = 0

self.func = func

def __call__(self, *args):

self.calls += 1

print('call %s to %s' % (self.calls, self.func.__name__))

self.func(*args)

@tracer # Same as spam = tracer(spam)

def spam(a, b, c): # Wrap spam in a decorator object

print(a, b, c)

spam(1, 2, 3) # Really calls the tracer wrapper object

spam('a', 'b', 'c') # Invokes __call__ in class

spam(4, 5, 6) # __call__ adds logic and runs original object

Because the spam function is run through the tracer decorator, when the original spam name is called it actually triggers the __call__ method in the class. This method counts and logs the call, and then dispatches it to the original wrapped function. Note how the *name argument syntax is used to pack and unpack the passed-in arguments; because of this, this decorator can be used to wrap any function with any number of positional arguments.

The net effect, again, is to add a layer of logic to the original spam function. Here is the script’s output—the first line comes from the tracer class, and the second comes from the spam function:

call 1 to spam

1 2 3

call 2 to spam

a b c

call 3 to spam

4 5 6

Trace through this example’s code for more insight. As it is, this decorator works for any function that takes positional arguments, but it does not return the decorated function’s result, doesn’t handle keyword arguments, and cannot decorate class method functions (in short, for methods its __call__ would be passed a tracer instance only). As we’ll see in Part VIII, there are a variety of ways to code function decorators,

Return Main Page Previous Page Next Page

®Online Book Reader