Online Book Reader

Home Category

Learning Python - Mark Lutz [365]

By Root 1823 0
their implicit attribute fetches through generic attribute managers: neither __getattr__ (run for undefined attributes) nor its cousin __getattribute__ (run for all attributes) is invoked. This is why we have to redefine __str__ redundantly in the alternative Manager, in order to ensure that printing is routed to the embedded Person object when run in Python 3.0.

Technically, this happens because classic classes normally look up operator overloading names in instances at runtime, but new-style classes do not—they skip the instance entirely and look up such methods in classes. In 2.6 classic classes, built-ins do route attributes generically—printing, for example, routes __str__ through __getattr__. New-style classes also inherit a default for __str__ that would foil __getattr__, but __getattribute__ doesn’t intercept the name in 3.0 either.

This is a change, but isn’t a show-stopper—delegation-based classes can generally redefine operator overloading methods to delegate them to wrapped objects in 3.0, either manually or via tools or superclasses. This topic is too advanced to explore further in this tutorial, though, so don’t sweat the details too much here. Watch for it to be revisited in the attribute management coverage of Chapter 37, and again in the context of Private class decorators in Chapter 38.

* * *

Step 6: Using Introspection Tools

Let’s make one final tweak before we throw our objects onto a database. As they are, our classes are complete and demonstrate most of the basics of OOP in Python. They still have two remaining issues we probably should iron out, though, before we go live with them:

First, if you look at the display of the objects as they are right now, you’ll notice that when you print tom the Manager labels him as a Person. That’s not technically incorrect, since Manager is a kind of customized and specialized Person. Still, it would be more accurate to display objects with the most specific (that is, lowest) classes possible.

Second, and perhaps more importantly, the current display format shows only the attributes we include in our __str__, and that might not account for future goals. For example, we can’t yet verify that tom’s job name has been set to mgr correctly by Manager’s constructor, because the __str__ we coded for Person does not print this field. Worse, if we ever expand or otherwise change the set of attributes assigned to our objects in __init__, we’ll have to remember to also update __str__ for new names to be displayed, or it will become out of sync over time.

The last point means that, yet again, we’ve made potential extra work for ourselves in the future by introducing redundancy in our code. Because any disparity in __str__ will be reflected in the program’s output, this redundancy may be more obvious than the other forms we addressed earlier; still, avoiding extra work in the future is generally a good thing.

Special Class Attributes

We can address both issues with Python’s introspection tools—special attributes and functions that give us access to some of the internals of objects’ implementations. These tools are somewhat advanced and generally used more by people writing tools for other programmers to use than by programmers developing applications. Even so, a basic knowledge of some of these tools is useful because they allow us to write code that processes classes in generic ways. In our code, for example, there are two hooks that can help us out, both of which were introduced near the end of the preceding chapter:

The built-in instance.__class__ attribute provides a link from an instance to the class from which it was created. Classes in turn have a __name__, just like modules, and a __bases__ sequence that provides access to superclasses. We can use these here to print the name of the class from which an instance is made rather than one we’ve hardcoded.

The built-in object.__dict__ attribute provides a dictionary with one key/value pair for every attribute attached to a namespace object (including modules, classes, and instances). Because

Return Main Page Previous Page Next Page

®Online Book Reader