Online Book Reader

Home Category

Learning Python - Mark Lutz [529]

By Root 1856 0
to register a function to an API, assign function attributes, and so on.

In more typical use, to insert logic that intercepts later calls to a function, we might code a decorator to return a different object than the original function:

def decorator(F):

# Save or use function F

# Return a different callable: nested def, class with __call__, etc.

@decorator

def func(): ... # func = decorator(func)

This decorator is invoked at decoration time, and the callable it returns is invoked when the original function name is later called. The decorator itself receives the decorated function; the callable returned receives whatever arguments are later passed to the decorated function’s name. This works the same for class methods: the implied instance object simply shows up in the first argument of the returned callable.

In skeleton terms, here’s one common coding pattern that captures this idea—the decorator returns a wrapper that retains the original function in an enclosing scope:

def decorator(F): # On @ decoration

def wrapper(*args): # On wrapped function call

# Use F and args

# F(*args) calls original function

return wrapper

@decorator # func = decorator(func)

def func(x, y): # func is passed to decorator's F

...

func(6, 7) # 6, 7 are passed to wrapper's *args

When the name func is later called, it really invokes the wrapper function returned by decorator; the wrapper function can then run the original func because it is still available in an enclosing scope. When coded this way, each decorated function produces a new scope to retain state.

To do the same with classes, we can overload the call operation and use instance attributes instead of enclosing scopes:

class decorator:

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

self.func = func

def __call__(self, *args): # On wrapped function call

# Use self.func and args

# self.func(*args) calls original function

@decorator

def func(x, y): # func = decorator(func)

... # func is passed to __init__

func(6, 7) # 6, 7 are passed to __call__'s *args

When the name func is later called now, it really invokes the __call__ operator overloading method of the instance created by decorator; the __call__ method can then run the original func because it is still available in an instance attribute. When coded this way, each decorated function produces a new instance to retain state.

Supporting method decoration

One subtle point about the prior class-based coding is that while it works to intercept simple function calls, it does not quite work when applied to class method functions:

class decorator:

def __init__(self, func): # func is method without instance

self.func = func

def __call__(self, *args): # self is decorator instance

# self.func(*args) fails! # C instance not in args!

class C:

@decorator

def method(self, x, y): # method = decorator(method)

... # Rebound to decorator instance

When coded this way, the decorated method is rebound to an instance of the decorator class, instead of a simple function.

The problem with this is that the self in the decorator’s __call__ receives the decorator class instance when the method is later run, and the instance of class C is never included in *args. This makes it impossible to dispatch the call to the original method—the decorator object retains the original method function, but it has no instance to pass to it.

To support both functions and methods, the nested function alternative works better:

def decorator(F): # F is func or method without instance

def wrapper(*args): # class instance in args[0] for method

# F(*args) runs func or method

return wrapper

@decorator

def func(x, y): # func = decorator(func)

...

func(6, 7) # Really calls wrapper(6, 7)

class C:

@decorator

def method(self, x, y): # method = decorator(method)

... # Rebound to simple function

X = C()

X.method(6, 7) # Really calls wrapper(X, 6, 7)

When coded this way wrapper receives the C class instance in its first argument, so it can dispatch to the original method and access state information.

Technically,

Return Main Page Previous Page Next Page

®Online Book Reader