Credit: Alex Martelli, AB Strakt, author of forthcoming Python in a Nutshell
Object-oriented programming (OOP) is among Python’s greatest strengths. Python’s OOP features keep improving steadily and gradually, just like Python in general. You could write object-oriented programs better in Python 1.5.2 (the ancient, long-stable version that was new when I first began to work with Python) than in any other popular language (excluding, of course, Lisp and its variants—I doubt there’s anything you can’t do well in Lisp-like languages, as long as you can stomach the parentheses-heavy concrete syntax). Now, with Python 2.2, OOP is substantially better than with 1.5.2. I am constantly amazed at the systematic progress Python achieves without sacrificing solidity, stability, and backward compatibility.
To get the most out of Python’s OOP features, you should use them “the Python way,” rather than trying to mimic C++, Java, Smalltalk, or other languages you may be familiar with. You can do a lot of mimicry, but you’ll get better mileage if you invest in understanding the Python way. Most of the investment is in increasing your understanding of OOP itself: what does OOP buy you, and which underlying mechanisms can your object-oriented programs use? The rest of the investment is in understanding the specific mechanisms that Python itself offers.
One caveat is in order. For such a high-level language, Python is quite explicit about the OOP mechanisms it uses behind the curtains: they’re exposed and available for your exploration and tinkering. Exploration and understanding are good, but beware the temptation to tinker. In other words, don’t use unnecessary black magic just because you can. Specifically, don’t use it in production code (code that you and others must maintain). If you can meet your goals with simplicity (and most often, in Python, you can), then keep your code simple.
So what is OOP all about? First of all, it’s about keeping some state (data) and some behavior (code) together in handy packets. “Handy packets” is the key here. Every program has state and behavior—programming paradigms differ only in how you view, organize, and package them. If the packaging is in terms of objects that typically comprise state and behavior, you’re using OOP. Some object-oriented languages force you to use OOP for everything, so you end up with many objects that lack either state or behavior. Python, however, supports multiple paradigms. While everything in Python is an object, you package things up as OOP objects only when you want to. Other languages try to force your programming style into a predefined mold for your own good, while Python empowers you to make and express your own design choices.
With OOP, once you have specified how an object is composed, you can instantiate as many objects of that kind as you need. When you don’t want to create multiple objects, consider using other Python constructs, such as modules. In this chapter, you’ll find recipes for Singleton, an object-oriented design pattern that takes away the multiplicity of instantiation. But if you want only one instance, in Python it’s often best to use a module, not an OOP object.
class SomeName: """ You usually define data and code here (in the class body). """
When you want a new instance of a class, call the class object as if it was a function. Each call returns a new instance object:
anInstance = SomeName( ) another = SomeName( )
another are two
distinct instance objects, both belonging to the
SomeName class. (See Recipe 1.8 for a class that does little more than this
but is quite useful.) You can bind and access
(state) of an instance object:
anInstance.someNumber = 23 * 45 print anInstance.someNumber # 1035
Instances of an “empty” class like this have no behavior, but they may have state. Most often, however, you want instances to have behavior. Specify this behavior by defining methods in the class body:
class Behave: def _ _init_ _(self, name): self.name = name def once(self): print "Hello, ", self.name def rename(self, newName) self.name = newName def repeat(self, N): for i in range(N): self.once( )
Define methods with the same
def statement Python uses to define
functions, since methods are basically functions. However, a method
is an attribute of a class object, and its first formal argument is
(by universal convention) named
self always refers to the instance on which you
call the method.
The method with the special name
_ _init_ _ is
known as the constructor
for the class. Python calls it to initialize each newly created
instance, with the arguments that you passed when calling the class
self, which you do not pass
explicitly, as Python supplies it automatically). The body of
_ _init_ _ typically binds attributes on the newly
self instance to initialize the
instance’s state appropriately.
Other methods implement the behavior of instances of the class.
Typically, they do so by accessing instance attributes. Also, methods
often rebind instance attributes, and they may call other methods.
Within a class definition, these actions are always done with the
self.something syntax. Once you instantiate the
class, however, you call methods on the instance, access the
instance’s attributes, and even rebind them using
beehive = Behave("Queen Bee") beehive.repeat(3) beehive.rename("Stinger") beehive.once( ) print beehive.name beehive.name = 'See, you can rebind it "from the outside" too, if you want' beehive.repeat(2)
If you’re new to OOP in Python, try implementing these things in an interactive Python environment, such as the GUI shell supplied by the free IDLE development environment that comes with Python.
In addition to the constructor (
_ _init_ _), your
class may have other special methods, which are methods with names
that start and end with two underscores. Python calls the special
methods of a class when instances of the class are used in various
operations and built-in functions. For example,
x._ _len_ _( ),
a._ _add_ _(b), and
a._ _getitem_ _(b). Therefore, by defining special methods in a class,
you can make instances of that class interchangeable with objects of
built-in types, such as numbers, lists, dictionaries, and so on.
The ability to handle different objects in similar ways, called
is a major advantage of OOP. With polymorphism, you can call the same
method on each object and let each object implement the method
appropriately. For example, in addition to the
Behave class, you might have another class that
repeat method, with a rather
class Repeater: def repeat(self, N): print N*"*-*"
You can mix instances of
Repeater at will, as long as the only method you
call on them is
aMix = beehive, Behave('John'), Repeater( ), Behave('world') for whatever in aMix: whatever.repeat(3)
Other languages require inheritance or the formal definition and implementation of interfaces for polymorphism to work. In Python, all you need is methods with the same signature (i.e., methods that are callable with the same arguments).
class Subclass(Behave): def once(self): print '(%s)' % self.name subInstance = Subclass("Queen Bee") subInstance.repeat(3)
Subclass class overrides only the
once method, but you can also call the
repeat method on
as it inherits that method from the
superclass. The body of the
repeat method calls
once N times on the specific
instance, using whatever version of the
method the instance has. In this case, it uses the method from the
Subclass class, which prints the name in
parentheses, not the version from the
class, which prints it after a greeting. The idea of a method calling
other methods on the same instance and getting the appropriately
overridden version of each is important in every object-oriented
language, including Python. This is known as the Template-Method
Often, the method of a subclass overrides a method from the superclass, but needs to call the method of the superclass as a part of its own operation. You do this in Python by explicitly getting the method as a class attribute and passing the instance as the first argument:
class OneMore(Behave): def repeat(self, N): Behave.repeat(self, N+1) zealant = OneMore("Worker Bee") zealant.repeat(3)
OneMore class implements its own
repeat method in terms of the method with the same
name in its superclass,
Behave, with a slight
change. This approach, known as
is pervasive in all programming. Delegation involves implementing
some functionality by letting another existing piece of code do most
of the work, often with some slight variation. Often, an overriding
method is best implemented by delegating some of the work to the same
method in the superclass. In Python, the syntax
Classname.method(self, ...) delegates to
Classname’s version of the
Python actually supports multiple inheritance: one class can inherit from several others. In terms of coding, this is a minor issue that lets you use the mix-in class idiom, a convenient way to supply some functionality across a broad range of classes. (See Recipe 5.14 for an unusual variant of this.) However, multiple inheritance is important because of its implications for object-oriented analysis—how you conceptualize your problem and your solution in the first place. Single inheritance pushes you to frame your problem space via taxonomy (i.e., mutually exclusive classification). The real world doesn’t work like that. Rather, it resembles Jorge Luis Borges’s explanation in “The Analytical Language of John Wilkins”, from a purported Chinese Encyclopedia, The Celestial Emporium of Benevolent Knowledge. Borges explains that all animals are divided into:
Those that belong to the Emperor
Those that are trained
Those included in the present classification
Those that tremble as if they were mad
Those drawn with a very fine camelhair brush
Those that have just broken a flower vase
Those that from a long way off look like flies
You get the point: taxonomy forces you to pigeonhole, fitting everything into categories that aren’t truly mutually exclusive. Modeling aspects of the real world in your programs is hard enough without buying into artificial constraints such as taxonomy. Multiple inheritance frees you from these constraints.
Python 2.2 has introduced an important
innovation in Python’s object model. Classic
classes, such as those mentioned in this introduction, still work as
they always did. In addition, you can use new-style classes, which
are classes that subclass a built-in type, such as
file. If you want a new-style class and do not
need to inherit from any specific built-in type, you can subclass the
object, which is the root of the whole
New-style classes work like existing ones, with some specific changes and several additional options. The recipes in this book were written and collected before the release of Python 2.2, and therefore use mostly classic classes. However this chapter specifies if a recipe might be inapplicable to a new-style class (a rare issue) or if new-style classes might offer alternative (and often preferable) ways to accomplish the same tasks (which is most often the case). The information you find in this chapter is therefore just as useful whether you use Python 2.1, 2.2, or even the still-experimental 2.3 (being designed as we write), which won’t change any of Python’s OOP features.