Online Book Reader

Home Category

Learning Python - Mark Lutz [541]

By Root 1867 0
bob.pay())

sue = Person('Sue', 50, 20) # Same, single object

print(sue.name, sue.pay())

X = Spam(42) # One Person, one Spam

Y = Spam(99)

print(X.attr, Y.attr)

Now, when the Person or Spam class is later used to create an instance, the wrapping logic layer provided by the decorator routes instance construction calls to onCall, which in turn calls getInstance to manage and share a single instance per class, regardless of how many construction calls are made. Here’s this code’s output:

Bob 400

Bob 400

42 42

Interestingly, you can code a more self-contained solution here if you’re able to use the nonlocal statement (available in Python 3.0 and later) to change enclosing scope names, as described earlier—the following alternative achieves an identical effect, by using one enclosing scope per class, instead of one global table entry per class:

def singleton(aClass): # On @ decoration

instance = None

def onCall(*args): # On instance creation

nonlocal instance # 3.0 and later nonlocal

if instance == None:

instance = aClass(*args) # One scope per class

return instance

return onCall

This version works the same, but it does not depend on names in the global scope outside the decorator. In either Python 2.6 or 3.0, you can also code a self-contained solution with a class instead—the following uses one instance per class, rather than an enclosing scope or global table, and works the same as the other two versions (in fact, it relies on the same coding pattern that we will later see is a common decorator class blunder; here we want just one instance, but that’s not always the case):

class singleton:

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

self.aClass = aClass

self.instance = None

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

if self.instance == None:

self.instance = self.aClass(*args) # One instance per class

return self.instance

To make this decorator a fully general-purpose tool, store it in an importable module file, indent the self-test code under a __name__ check, and add support for keyword arguments in construction calls with **kargs syntax (I’ll leave this as a suggested exercise).

Tracing Object Interfaces

The singleton example of the prior section illustrated using class decorators to manage all the instances of a class. Another common use case for class decorators augments the interface of each generated instance. Class decorators can essentially install on instances a wrapper logic layer that manages access to their interfaces in some way.

For example, in Chapter 30, the __getattr__ operator overloading method is shown as a way to wrap up entire object interfaces of embedded instances, in order to implement the delegation coding pattern. We saw similar examples in the managed attribute coverage of the prior chapter. Recall that __getattr__ is run when an undefined attribute name is fetched; we can use this hook to intercept method calls in a controller class and propagate them to an embedded object.

For reference, here’s the original nondecorator delegation example, working on two built-in type objects:

class Wrapper:

def __init__(self, object):

self.wrapped = object # Save object

def __getattr__(self, attrname):

print('Trace:', attrname) # Trace fetch

return getattr(self.wrapped, attrname) # Delegate fetch

>>> x = Wrapper([1,2,3]) # Wrap a list

>>> x.append(4) # Delegate to list method

Trace: append

>>> x.wrapped # Print my member

[1, 2, 3, 4]

>>> x = Wrapper({"a": 1, "b": 2}) # Wrap a dictionary

>>> list(x.keys()) # Delegate to dictionary method

Trace: keys # Use list() in 3.0

['a', 'b']

In this code, the Wrapper class intercepts access to any of the wrapped object’s attributes, prints a trace message, and uses the getattr built-in to pass off the request to the wrapped object. Specifically, it traces attribute accesses made outside the wrapped object’s class; accesses inside the wrapped object’s methods are not caught and run normally by design. This whole-interface model differs from the behavior of function decorators, which wrap up just

Return Main Page Previous Page Next Page

®Online Book Reader