Online Book Reader

Home Category

Learning Python - Mark Lutz [543]

By Root 1942 0
40, 50)

sue = Person('Sue', rate=100, hours=60)

class Person: ... # Non-decorator approach

bob = Wrapper(Person('Bob', 40, 50))

sue = Wrapper(Person('Sue', rate=100, hours=60))

Assuming you will make more than one instance of a class, decorators will generally be a net win in terms of both code size and code maintenance.

* * *

Note


Attribute version skew note: As we learned in Chapter 37, __getattr__ will intercept accesses to operator overloading methods like __str__ and __repr__ in Python 2.6, but not in 3.0.

In Python 3.0, class instances inherit defaults for some (but not all) of these names from the class (really, from the automatic object superclass), because all classes are “new-style.” Moreover, in 3.0 implicitly invoked attributes for built-in operations like printing and + are not routed through __getattr__ (or its cousin, __getattribute__). New-style classes look up such methods in classes and skip the normal instance lookup entirely.

Here, this means that the __getattr__-based tracing wrapper will automatically trace and propagate operator overloading calls in 2.6, but not in 3.0. To see this, display “x” directly at the end of the preceding interactive session—in 2.6 the attribute __repr__ is traced and the list prints as expected, but in 3.0 no trace occurs and the list prints using a default display for the Wrapper class:

>>> x # 2.6

Trace: __repr__

[4, 5, 6, 7]

>>> x # 3.0

To work the same in 3.0, operator overloading methods generally need to be redefined redundantly in the wrapper class, either by hand, by tools, or by definition in superclasses. Only simple named attributes will work the same in both versions. We’ll see this version skew at work again in a Private decorator later in this chapter.

* * *

Class Blunders II: Retaining Multiple Instances

Curiously, the decorator function in this example can almost be coded as a class instead of a function, with the proper operator overloading protocol. The following slightly simplified alternative works similarly because its __init__ is triggered when the @ decorator is applied to the class, and its __call__ is triggered when a subject class instance is created. Our objects are really instances of Tracer this time, and we essentially just trade an enclosing scope reference for an instance attribute here:

class Tracer:

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

self.aClass = aClass # Use instance attribute

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

self.wrapped = self.aClass(*args) # ONE (LAST) INSTANCE PER CLASS!

return self

def __getattr__(self, attrname):

print('Trace: ' + attrname)

return getattr(self.wrapped, attrname)

@Tracer # Triggers __init__

class Spam: # Like: Spam = Tracer(Spam)

def display(self):

print('Spam!' * 8)

...

food = Spam() # Triggers __call__

food.display() # Triggers __getattr__

As we saw in the abstract earlier, though, this class-only alternative handles multiple classes as before, but it won’t quite work for multiple instances of a given class: each instance construction call triggers __call__, which overwrites the prior instance. The net effect is that Tracer saves just one instance—the last one created. Experiment with this yourself to see how, but here’s an example of the problem:

@Tracer

class Person: # Person = Tracer(Person)

def __init__(self, name): # Wrapper bound to Person

self.name = name

bob = Person('Bob') # bob is really a Wrapper

print(bob.name) # Wrapper embeds a Person

Sue = Person('Sue')

print(sue.name) # sue overwrites bob

print(bob.name) # OOPS: now bob's name is 'Sue'!

This code’s output follows—because this tracer only has a single shared instance, the second overwrites the first:

Trace: name

Bob

Trace: name

Sue

Trace: name

Sue

The problem here is bad state retention—we make one decorator instance per class, but not per class instance, such that only the last instance is retained. The solution, as in our prior class blunder for decorating methods, lies in abandoning class-based

Return Main Page Previous Page Next Page

®Online Book Reader