Online Book Reader

Home Category

Learning Python - Mark Lutz [574]

By Root 1500 0
message whenever any normally named attribute of a class instance is fetched:

# Class decorator to trace external instance attribute fetches

def Tracer(aClass): # On @ decorator

class Wrapper:

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

self.wrapped = aClass(*args, **kargs) # Use enclosing scope name

def __getattr__(self, attrname):

print('Trace:', attrname) # Catches all but .wrapped

return getattr(self.wrapped, attrname) # Delegate to wrapped object

return Wrapper

@Tracer

class Person: # Person = Tracer(Person)

def __init__(self, name, hours, rate): # Wrapper remembers Person

self.name = name

self.hours = hours

self.rate = rate # In-method fetch not traced

def pay(self):

return self.hours * self.rate

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

print(bob.name) # Wrapper embeds a Person

print(bob.pay()) # Triggers __getattr__

When this code is run, the decorator uses class name rebinding to wrap instance objects in an object that produces the trace lines in the following output:

Trace: name

Bob

Trace: pay

2000

Although it’s possible for a metaclass to achieve the same effect, it seems less straightforward conceptually. Metaclasses are designed explicitly to manage class object creation, and they have an interface tailored for this purpose. To use a metaclass to manage instances, we have to rely on a bit more magic. The following metaclass has the same effect and output as the prior decorator:

# Manage instances like the prior example, but with a metaclass

def Tracer(classname, supers, classdict): # On class creation call

aClass = type(classname, supers, classdict) # Make client class

class Wrapper:

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

self.wrapped = aClass(*args, **kargs)

def __getattr__(self, attrname):

print('Trace:', attrname) # Catches all but .wrapped

return getattr(self.wrapped, attrname) # Delegate to wrapped object

return Wrapper

class Person(metaclass=Tracer): # Make Person with Tracer

def __init__(self, name, hours, rate): # Wrapper remembers Person

self.name = name

self.hours = hours

self.rate = rate # In-method fetch not traced

def pay(self):

return self.hours * self.rate

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

print(bob.name) # Wrapper embeds a Person

print(bob.pay()) # Triggers __getattr__

This works, but it relies on two tricks. First, it must use a simple function instead of a class, because type subclasses must adhere to object creation protocols. Second, it must manually create the subject class by calling type manually; it needs to return an instance wrapper, but metaclasses are also responsible for creating and returning the subject class. Really, we’re using the metaclass protocol to imitate decorators in this example, rather than vice versa; because both run at the conclusion of a class statement, in many roles they are just variations on a theme. This metaclass version produces the same output as the decorator when run live:

Trace: name

Bob

Trace: pay

2000

You should study both versions of these examples for yourself to weigh their tradeoffs. In general, though, metaclasses are probably best suited to class management, due to their design; class decorators can manage either instances or classes, though they may not be the best option for more advanced metaclass roles that we don’t have space to cover in this book (if you want to learn more about decorators and metaclasses after reading this chapter, search the Web or Python’s standard manuals). The next section concludes this chapter with one more common use case—applying operations to a class’s methods automatically.

Example: Applying Decorators to Methods

As we saw in the prior section, because they are both run at the end of a class statement, metaclasses and decorators can often be used interchangeably, albeit with different syntax. The choice between the two is arbitrary in many contexts. It’s also possible to use them in combination, as complementary tools. In this section, we’ll explore an example of

Return Main Page Previous Page Next Page

®Online Book Reader