Online Book Reader

Home Category

Learning Python - Mark Lutz [531]

By Root 1927 0
a new scope, which remembers the original class. We’ll flesh out this example into some more useful code later in this chapter.

Like function decorators, class decorators are commonly coded as either “factory” functions that create and return callables, classes that use __init__ or __call__ methods to intercept call operations, or some combination thereof. Factory functions typically retain state in enclosing scope references, and classes in attributes.

Supporting multiple instances

As with function decorators, with class decorators some callable type combinations work better than others. Consider the following invalid alternative to the class decorator of the prior example:

class Decorator:

def __init__(self, C): # On @ decoration

self.C = C

def __call__(self, *args): # On instance creation

self.wrapped = self.C(*args)

return self

def __getattr__(self, attrname): # On atrribute fetch

return getattr(self.wrapped, attrname)

@Decorator

class C: ... # C = Decorator(C)

x = C()

y = C() # Overwrites x!

This code handles multiple decorated classes (each makes a new Decorator instance) and will intercept instance creation calls (each runs __call__). Unlike the prior version, however, this version fails to handle multiple instances of a given class—each instance creation call overwrites the prior saved instance. The original version does support multiple instances, because each instance creation call makes a new independent wrapper object. More generally, either of the following patterns supports multiple wrapped instances:

def decorator(C): # On @ decoration

class Wrapper:

def __init__(self, *args): # On instance creation

self.wrapped = C(*args)

return Wrapper

class Wrapper: ...

def decorator(C): # On @ decoration

def onCall(*args): # On instance creation

return Wrapper(C(*args)) # Embed instance in instance

return onCall

We’ll study this phenomenon in a more realistic context later in the chapter; in practice, though, we must be careful to combine callable types properly to support our intent.

Decorator Nesting

Sometimes one decorator isn’t enough. To support multiple steps of augmentation, decorator syntax allows you to add multiple layers of wrapper logic to a decorated function or method. When this feature is used, each decorator must appear on a line of its own. Decorator syntax of this form:

@A

@B

@C

def f(...):

...

runs the same as the following:

def f(...):

...

f = A(B(C(f)))

Here, the original function is passed through three different decorators, and the resulting callable object is assigned back to the original name. Each decorator processes the result of the prior, which may be the original function or an inserted wrapper.

If all the decorators insert wrappers, the net effect is that when the original function name is called, three different layers of wrapping object logic will be invoked, to augment the original function in three different ways. The last decorator listed is the first applied, and the most deeply nested (insert joke about “interior decorators” here...).

Just as for functions, multiple class decorators result in multiple nested function calls, and possibly multiple levels of wrapper logic around instance creation calls. For example, the following code:

@spam

@eggs

class C:

...

X = C()

is equivalent to the following:

class C:

...

C = spam(eggs(C))

X = C()

Again, each decorator is free to return either the original class or an inserted wrapper object. With wrappers, when an instance of the original C class is finally requested, the call is redirected to the wrapping layer objects provided by both the spam and eggs decorators, which may have arbitrarily different roles.

For example, the following do-nothing decorators simply return the decorated function:

def d1(F): return F

def d2(F): return F

def d3(F): return F

@d1

@d2

@d3

def func(): # func = d1(d2(d3(func)))

print('spam')

func() # Prints "spam"

The same syntax works on classes, as do these same do-nothing decorators.

When decorators insert wrapper

Return Main Page Previous Page Next Page

®Online Book Reader