Online Book Reader

Home Category

Learning Python - Mark Lutz [572]

By Root 1905 0
example compares metaclass- and decorator-based implementations of class augmentation and instance wrapping, and the second applies a decorator with a metaclass first and then with another decorator. As you’ll see, the two tools are often interchangeable, and even complementary.

Manual Augmentation

Earlier in this chapter, we looked at skeleton code that augmented classes by adding methods to them in various ways. As we saw, simple class-based inheritance suffices if the extra methods are statically known when the class is coded. Composition via object embedding can often achieve the same effect too. For more dynamic scenarios, though, other techniques are sometimes required—helper functions can usually suffice, but metaclasses provide an explicit structure and minimize the maintenance costs of changes in the future.

Let’s put these ideas in action here with working code. Consider the following example of manual class augmentation—it adds two methods to two classes, after they have been created:

# Extend manually - adding new methods to classes

class Client1:

def __init__(self, value):

self.value = value

def spam(self):

return self.value * 2

class Client2:

value = 'ni?'

def eggsfunc(obj):

return obj.value * 4

def hamfunc(obj, value):

return value + 'ham'

Client1.eggs = eggsfunc

Client1.ham = hamfunc

Client2.eggs = eggsfunc

Client2.ham = hamfunc

X = Client1('Ni!')

print(X.spam())

print(X.eggs())

print(X.ham('bacon'))

Y = Client2()

print(Y.eggs())

print(Y.ham('bacon'))

This works because methods can always be assigned to a class after it’s been created, as long as the methods assigned are functions with an extra first argument to receive the subject self instance—this argument can be used to access state information accessible from the class instance, even though the function is defined independently of the class.

When this code runs, we receive the output of a method coded inside the first class, as well as the two methods added to the classes after the fact:

Ni!Ni!

Ni!Ni!Ni!Ni!

baconham

ni?ni?ni?ni?

baconham

This scheme works well in isolated cases and can be used to fill out a class arbitrarily at runtime. It suffers from a potentially major downside, though: we have to repeat the augmentation code for every class that needs these methods. In our case, it wasn’t too onerous to add the two methods to both classes, but in more complex scenarios this approach can be time-consuming and error-prone. If we ever forget to do this consistently, or we ever need to change the augmentation, we can run into problems.

Metaclass-Based Augmentation

Although manual augmentation works, in larger programs it would be better if we could apply such changes to an entire set of classes automatically. That way, we’d avoid the chance of the augmentation being botched for any given class. Moreover, coding the augmentation in a single location better supports future changes—all classes in the set will pick up changes automatically.

One way to meet this goal is to use metaclasses. If we code the augmentation in a metaclass, every class that declares that metaclass will be augmented uniformly and correctly and will automatically pick up any changes made in the future. The following code demonstrates:

# Extend with a metaclass - supports future changes better

def eggsfunc(obj):

return obj.value * 4

def hamfunc(obj, value):

return value + 'ham'

class Extender(type):

def __new__(meta, classname, supers, classdict):

classdict['eggs'] = eggsfunc

classdict['ham'] = hamfunc

return type.__new__(meta, classname, supers, classdict)

class Client1(metaclass=Extender):

def __init__(self, value):

self.value = value

def spam(self):

return self.value * 2

class Client2(metaclass=Extender):

value = 'ni?'

X = Client1('Ni!')

print(X.spam())

print(X.eggs())

print(X.ham('bacon'))

Y = Client2()

print(Y.eggs())

print(Y.ham('bacon'))

This time, both of the client classes are extended with the new methods because they are instances of a metaclass that performs

Return Main Page Previous Page Next Page

®Online Book Reader