So far, we’ve concentrated on the OOP tool in Python—the class. But OOP is also about design issues—how to use classes to model useful objects. This section will touch on a few OOP core ideas, and look at some additional examples that are more realistic than the examples shown so far. Many of the design terms mentioned here require more explanation than we can provide; if this section sparks your curiosity, we suggest exploring a text on OOP design or design patterns as a next step.
Python’s implementation of OOP can be summarized by three ideas:
By now, you should have a good feel for what inheritance is all about in Python. We’ve talked about Python’s polymorphism a few times already; it flows from Python’s lack of type declarations. Because attributes are always resolved at runtime, objects that implement the same interfaces are interchangeable. Clients don’t need to know what sort of object is implementing a method they call.
Encapsulation means packaging in Python—hiding implementation details behind an object’s interface; it does not mean enforced privacy, as you’ll see in Chapter 23. Encapsulation allows the implementation of an object’s interface to be changed, without impacting the users of that object.
Some OOP languages also define polymorphism to mean overloading functions based on the type signatures of their arguments. Since there is no type declaration in Python, the concept doesn’t really apply; polymorphism in Python is based on object interfaces, not types. For example, you can try to overload methods by their argument lists:
class C: def meth(self, x): ... def meth(self, x, y, z): ...
This code will run, but because the def
simply
assigns an object to a name in the class’s scope,
the last definition of a method function is the only one retained
(it’s just as if you say X=1
, and
then X=2
; X
will be 2).
Type-based selections can always be coded using the type testing ideas we met in Chapter 7, or the argument list tools in Chapter 13:
class C: def meth(self, *args): if len(args) == 1: ... elif type(arg[0]) == int: ...
You normally shouldn’t do this, though—as described in Chapter 12, write your code to expect an object interface, not a specific datatype. That way, it becomes useful for a broader category of types and applications, now and in the future:
class C: def meth(self, x): x.operation( ) # Assume x does the right thing.
It’s also generally considered better to use distinct method names for distinct operations, rather than relying on call signatures (no matter what language you code in).
Chapter 6 showed you how to use dictionaries to record properties of entities in your program. Let’s explore this in more detail. Here is the example for dictionary-based records used earlier:
>>>rec = { }
>>>rec['name'] = 'mel'
>>>rec['age'] = 40
>>>rec['job'] = 'trainer/writer'
>>> >>>print rec['name']
mel
This code emulates things like “records” and “structs” in other languages. It turns out that there are multiple ways to do the same with classes. Perhaps the simplest is this:
>>>class rec: pass
... >>>rec.name = 'mel'
>>>rec.age = 40
>>>rec.job = 'trainer/writer'
>>> >>>print rec.age
40
This code has substantially less syntax than the dictionary
equivalent. It uses an empty class
statement to
generate an empty namespace object (notice the
pass
statement—we need a statement
syntactically even though there is no logic to code in this case).
Once we make the empty class, we fill it out by assigning to class
attributes over time.
This works, but we’ll need a new
class
statement for each distinct record we will
need. Perhaps more typically, we can instead generate
instances
of an empty class to represent each
distinct entity:
>>>class rec: pass
... >>>pers1 = rec( )
>>>pers1.name = 'mel'
>>>pers1.job = 'trainer'
>>>pers1.age = 40
>>> >>>pers2 = rec( )
>>>pers2.name = 'dave'
>>>pers2.job = 'developer'
>>> >>>pers1.name, pers2.name
('mel', 'dave')
Here, we make two records from the same class; instances start out
life empty, just like classes. We fill in the record by assigning to
attributes. This time, though, there are two separate objects, and
hence two separate name
attributes. In fact,
instances of the same class don’t even have to have
the same set of attribute names; in this example, one has a unique
age
name. Instances really are distinct
namespaces—each has a distinct attribute dictionary. Although
they are normally filled out consistently by class methods, they are
more flexible than you might expect.
Finally, you might instead code a more full-blown class to implement your record:
>>>class Person:
...def __init__(self, name, job):
...self.name = name
...self.job = job
...def info(self):
...return (self.name, self.job)
... >>>mark = Person('ml', 'trainer')
>>>dave = Person('da', 'developer')
>>> >>>mark.job, dave.info( )
('trainer', ('da', 'developer'))
This scheme also makes multiple instances, but the class is not empty
this time: we’ve added logic (methods) to initialize
instances at construction time, and collect attributes into a tuple.
The constructor imposes some consistency on instances here, by always
setting name
and job
attributes.
Eventually, we might add additional logic to compute salaries, parse names, and so on. Ultimately, we might link the class into a larger hierarchy to inherit an existing set of methods by the automatic attribute search of classes, and may even store instances of the class in a file with Python object pickling to make them persistent (more on pickling and persistence in a sidebar, and again later in the book). In the end, although things like dictionaries are flexible, classes allow us to add behavior to objects in ways that built-in types and simple functions do not directly support.
We’ve talked about the mechanics of inheritance in depth already, but we’d like to show you an example of how it can be used to model real-world relationships. From a programmer’s point of view, inheritance is kicked off by attribute qualifications, and triggers a search for a name in an instance, its class, and then its superclasses. From a designer’s point of view, inheritance is a way to specify set membership: a class defines a set of properties that may be inherited and customized by more specific sets (i.e., subclasses).
To illustrate, let’s put that pizza-making robot we talked about at the start of this part of the book to work. Suppose we’ve decided to explore alternative career paths and open a pizza restaurant. One of the first things we’ll need to do is hire employees to serve customers, make the pizza, and so on. Being engineers at heart, we’ve also decided to build a robot to make the pizzas; but being politically and cybernetically correct, we’ve also decided to make our robot a full-fledged employee, with a salary.
Our pizza shop team can be defined by the following classes in the
example file employees.py. It defines four
classes and some self-test code. The most general class,
Employee
, provides common behavior such as bumping
up salaries (giveRaise
) and printing
(_
_repr__
). There are two
kinds of employees, and so two subclasses of
Employee
—Chef
and
Server
. Both override the inherited
work
method to print more specific messages.
Finally, our pizza robot is modeled by an even more specific class:
PizzaRobot
is a kind of Chef
,
which is a kind of Employee
. In OOP terms, we call
these relationships “is-a” links: a
robot is a chef, which is a(n) employee.
class Employee: def __init__(self, name, salary=0): self.name = name self.salary = salary def giveRaise(self, percent): self.salary = self.salary + (self.salary * percent) def work(self): print self.name, "does stuff" def __repr__(self): return "<Employee: name=%s, salary=%s>" % (self.name, self.salary) class Chef(Employee): def __init__(self, name): Employee.__init__(self, name, 50000) def work(self): print self.name, "makes food" class Server(Employee): def __init__(self, name): Employee.__init__(self, name, 40000) def work(self): print self.name, "interfaces with customer" class PizzaRobot(Chef): def __init__(self, name): Chef.__init__(self, name) def work(self): print self.name, "makes pizza" if __name__ == "__main__": bob = PizzaRobot('bob') # Make a robot named bob. print bob # Runs inherited __repr__ bob.work( ) # Run type-specific action. bob.giveRaise(0.20) # Give bob a 20% raise. print bob; print for klass in Employee, Chef, Server, PizzaRobot: obj = klass(klass.__name__) obj.work( )
When we run this module’s self-test code, we create
a pizza-making robot named bob
, which inherits
names from three classes: PizzaRobot
,
Chef
, and Employee
. For
instance, printing bob
runs the
Employee.__repr__
method, and giving
bob
a raise invokes
Employee.giveRaise
, because
that’s where inheritance finds it.
C:pythonexamples> python employees.py
<Employee: name=bob, salary=50000>
bob makes pizza
<Employee: name=bob, salary=60000.0>
Employee does stuff
Chef makes food
Server interfaces with customer
PizzaRobot makes pizza
In a class hierarchy like this, you can usually make instances of any
of the classes, not just the ones at the bottom. For instance, the
for
loop in this module’s
self-test code creates instances of all four classes; each responds
differently when asked to work, because the work
method is different in each. Really, these classes just simulate real
world objects; work
prints a message for the time
being, but could be expanded to really work later.
We introduced the notion of composition in Chapter 19. From a programmer’s point of view, composition involves embedding other objects in a container object and activating them to implement container methods. To a designer, composition is another way to represent relationships in a problem domain. But rather than set membership, composition has to do with components—parts of a whole. Composition also reflects the relationships between parts; it’s usually called a “has-a” relationship. Some OO design texts refer to composition as aggregation (or distinguish between the two terms by using aggregation for a weaker dependency between container and contained); in this text, “composition” simply refers to a collection of embedded objects.
Now that we’ve implemented our employees, let’s put them in the pizza shop and let them get busy. Our pizza shop is a composite object; it has an oven, and employees like servers and chefs. When a customer enters and places an order, the components of the shop spring into action—the server takes an order, the chef makes the pizza, and so on. The following example, file pizzashop.py, simulates all the objects and relationships in this scenario:
from employees import PizzaRobot, Server class Customer: def __init__(self, name): self.name = name def order(self, server): print self.name, "orders from", server def pay(self, server): print self.name, "pays for item to", server class Oven: def bake(self): print "oven bakes" class PizzaShop: def __init__(self): self.server = Server('Pat') # Embed other objects. self.chef = PizzaRobot('Bob') # A robot named bob self.oven = Oven( ) def order(self, name): customer = Customer(name) # Activate other objects. customer.order(self.server) # Customer orders from server. self.chef.work( ) self.oven.bake( ) customer.pay(self.server) if __name__ == "__main__": scene = PizzaShop( ) # Make the composite. scene.order('Homer') # Simulate Homer's order. print '...' scene.order('Shaggy') # Simulate Shaggy's order.
The PizzaShop
class is a container and controller;
its constructor makes and embeds instances of the employee classes we
wrote in the last section, as well as an Oven
class defined here. When this module’s self-test
code calls the PizzaShop
order
method, the embedded objects are asked to carry out their actions in
turn. Notice that we make a new Customer
object
for each order, and pass on the embedded Server
object to Customer
methods; customers come and go,
but the server is part of the pizza shop composite. Also notice that
employees are still involved in an inheritance relationship;
composition and inheritance are complementary tools:
C:pythonexamples> python pizzashop.py
Homer orders from <Employee: name=Pat, salary=40000>
Bob makes pizza
oven bakes
Homer pays for item to <Employee: name=Pat, salary=40000>
...
Shaggy orders from <Employee: name=Pat, salary=40000>
Bob makes pizza
oven bakes
Shaggy pays for item to <Employee: name=Pat, salary=40000>
When we run this module, our pizza shop handles two orders—one from Homer, and then one from Shaggy. Again, this is mostly just a toy simulation, but the objects and interactions are representative of composites at work. As a rule of thumb, classes can represent just about any objects and relationships you can express in a sentence; just replace nouns with classes and verbs with methods, and you have a first cut at a design.
For a more realistic composition example, recall the generic data stream processor function partially coded in the introduction to OOP in Chapter 19:
def processor(reader, converter, writer): while 1: data = reader.read( ) if not data: break data = converter(data) writer.write(data)
Rather than using a simple function here, we might code this as a class that uses composition to do its work, to provide more structure, and support inheritance. File streams.py demonstrates one way to code this:
class Processor: def __init__(self, reader, writer): self.reader = reader self.writer = writer def process(self): while 1: data = self.reader.readline( ) if not data: break data = self.converter(data) self.writer.write(data) def converter(self, data): assert 0, 'converter must be defined'
Coded this way, reader and writer objects are embedded within the class instance (composition), and we supply the converter logic in a subclass rather than passing in a converter function (inheritance). File converters.py shows how:
from streams import Processor class Uppercase(Processor): def converter(self, data): return data.upper( ) if __name__ == '__main__': import sys Uppercase(open('spam.txt'), sys.stdout).process( )
Here, the Uppercase
class inherits the stream
processing loop logic (and anything else that may be coded in its
superclasses). It needs to define only the thing that is unique about
it—the data conversion logic. When this file is run, it makes
and runs an instance, which reads from file
spam.txt and writes the uppercase equivalent of
that file to the stdout
stream:
C:lp2e>type spam.txt
spam Spam SPAM! C:lp2e>python converters.py
SPAM SPAM SPAM!
To process different sorts of streams, pass in different sorts of objects to the class construction call. Here, we use an output file instead of a stream:
C:lp2e>python
>>>import converters
>>>prog = converters.Uppercase(open('spam.txt'), open('spamup.txt', 'w'))
>>>prog.process( )
C:lp2e>type spamup.txt
SPAM SPAM SPAM!
But as suggested earlier, we could also pass in arbitrary objects wrapped up in classes that define the required input and output method interfaces. Here’s a simple example that passes in a writer class that wraps up the text inside HTML tags:
C:lp2e>python
>>>from converters import Uppercase
>>> >>>class HTMLize:
...def write(self, line):
...print '<PRE>%s</PRE>' % line[:-1]
... >>>Uppercase(open('spam.txt'), HTMLize( )).process( )
<PRE>SPAM</PRE> <PRE>SPAM</PRE> <PRE>SPAM!</PRE>
If you trace through this example’s control flow,
you’ll see that we get both uppercase conversion (by
inheritance) and HTML formatting (by
composition), even though the core processing
logic in the original Processor
superclass knows
nothing about either step. The processing code only cares that
writers have a write
method, and that a method
named convert
is defined; it
doesn’t care what those calls do. Such polymorphism
and encapsulation of logic is behind much of the power of classes.
As is, the Processor
superclass only gives a
file-scanning loop. In more real work, we might extend it to support
additional programming tools for its subclasses, and in the process
turn it into a full-blown framework. By coding
such tools once in a superclass, they can be reused in all your
programs. Even in this simple example, because so much is packaged
and inherited with classes, all we had to code
here was the HTML
formatting step; the rest is free.[1]
O
bject-oriented programmers often talk about
something called delegation, which usually implies controller objects
that embed other objects, to which they pass off operation requests.
The controllers can take care of administrative activities such as
keeping track of accesses and so on. In Python, delegation is often
implemented with the __getattr__
method hook;
because it intercepts accesses to nonexistent attributes, a wrapper
class can use __getattr__
to route arbitrary
accesses to a wrapped object. Consider file
trace.py, for instance:
class wrapper: def __init__(self, object): self.wrapped = object # Save object. def __getattr__(self, attrname): print 'Trace:', attrname # Trace fetch. return getattr(self.wrapped, attrname) # Delegate fetch.
Recall that __getattr__
gets the attribute name
as a string. This code makes use of the getattr
built-in function to fetch an attribute from the wrapped object by
name string—getattr(X,N)
is like
X.N
, except that N
is an
expression that evaluates to a string at runtime, not a variable. In
fact, getattr(X,N)
is similar to X.__dict__[N]
, but the former also performs inheritance
search like X.N
(see Section 21.5.4).
You can use the approach of this module’s
wrapper
class to manage access to any object with
attributes—lists, dictionaries, and even classes and instances.
Here, the class simply prints a trace message on each attribute
access, and delegates the attribute request to the embedded
wrapped
object:
>>>from trace import wrapper
>>>x = wrapper([1,2,3]) # Wrap a list.
>>>x.append(4) # Delegate to list method.
Trace: append >>>x.wrapped # Print my member.
[1, 2, 3, 4] >>>x = wrapper({"a": 1, "b": 2}) # Wrap a dictionary.
>>>x.keys( ) # Delegate to dictionary method.
Trace: keys ['a', 'b']
We’ll revive the notions of wrapped object and delegated operations as one way to extend built-in types in Chapter 23.
In the class
statement, more than one superclass
can be listed in parentheses in the header line. When you do this,
you use something called
multiple inheritance—the class and its
instances inherit names from all listed superclasses.
When searching for an attribute, Python searches superclasses in the class header from left to right until a match is found. Technically, the search proceeds depth-first all the way to the top, and then left to right, since any of the superclasses may have superclasses of its own.
In general, multiple inheritance is good for modeling objects that belong to more than one set. For instance, a person may be an engineer, a writer, a musician, and so on, and inherit properties from all such sets.
Perhaps the most common way multiple inheritance is used is to “mix in” general-purpose methods from superclasses. Such superclasses are usually called mixin classes—they provide methods you add to application classes by inheritance. For instance, Python’s default way to print a class instance object isn’t incredibly useful:
>>>class Spam:
...def __init__(self): # No __repr__
...self.data1 = "food"
... >>>X = Spam( )
>>>print X # Default: class, address
<__main__.Spam instance at 0x00864818>
As seen in the previous section on operator overloading, you can
provide a __repr__
method to implement a custom
string representation of your own. But rather than code a __repr__
in each and every class you wish to print, why not
code it once in a general-purpose tool class, and inherit it in all
your classes?
That’s what
mixins are for. The
following code, file mytools.py, defines a mixin
class called Lister
that overloads the __repr__
method for each class that includes
Lister
in its header line. It simply scans the
instance’s attribute dictionary (remember,
it’s exported in __dict__
) to
build up a string showing the names and values of all instance
attributes. Since classes are objects,
Lister
’s formatting logic can be
used for instances of any subclass; it’s a generic
tool.
Lister uses two special tricks to extract the
instance’s classname and address. Instances have a
built-in __class__
attribute that references the
class the instance was created from, and classes have a __name__
that is the name in the header, so self.__class__.__name__
fetches the name of an
instance’s class. You get the
instance’s memory address by calling the built-in
id
function, which returns any
object’s address (by definition, a unique object
identifier):
########################################### # Lister can be mixed-in to any class to # provide a formatted print of instances # via inheritance of __repr__ coded here; # self is the instance of the lowest class; ########################################### class Lister: def __repr__(self): return ("<Instance of %s, address %s: %s>" % (self.__class__.__name__, # My class's name id(self), # My address self.attrnames( )) ) # name=value list def attrnames(self): result = '' for attr in self.__dict__.keys( ): # Instance namespace dict if attr[:2] == '__': result = result + " name %s=<built-in> " % attr else: result = result + " name %s=%s " % (attr, self.__dict__ [attr]) return result
When derived from this class, instances display their attributes automatically when printed, which gives a bit more information than a simple address:
>>>from mytools import Lister
>>>class Spam(Lister):
...def __init__(self):
...self.data1 = 'food'
... >>>x = Spam( )
>>>x
<Instance of Spam, address 8821568: name data1=food >
Now, the Lister
class is useful for any class you
write—even classes that already have a superclass. This is
where multiple inheritance comes in handy: by adding (mixing in)
Lister
to the list of superclasses in a class
header, you get its __repr__
for free, while
still inheriting from the existing superclass. File
testmixin.py demonstrates:
from mytools import Lister # Get tool class class Super: def __init__(self): # superclass __init__ self.data1 = "spam" class Sub(Super, Lister): # Mix-in a __repr__ def __init__(self): # Lister has access to self Super.__init__(self) self.data2 = "eggs" # More instance attrs self.data3 = 42 if __name__ == "__main__": X = Sub( ) print X # Mixed-in repr
Here, Sub
inherits names from both
Super
and Lister
;
it’s a composite of its own names and names in both
its superclasses. When you make a Sub
instance and
print it, you automatically get the custom representation mixed in
from Lister
:
C:lp2e> python testmixin.py
<Instance of Sub, address 7833392:
name data3=42
name data2=eggs
name data1=spam
>
Lister
works in any class it’s
mixed into, because self
refers to an instance of
the subclass that pulls Lister
in, whatever that
may be. If you later decide to extend
Lister
’s __repr__
to also print all the class attributes that an instance
inherits, you’re safe; because it’s
an inherited method, changing Lister.__repr__
automatically updates the display of each subclass that imports the
class and mixes it in.[2]
In some sense, mixin classes are the class equivalent of
modules—packages of methods useful in a variety of clients.
Here is Lister
working again in single-inheritance
mode, on a different class’s instances; OOP is about
code reuse:
>>>from mytools import Lister
>>>class x(Lister):
...pass
... >>>t = x( )
>>>t.a = 1; t.b = 2; t.c = 3
>>>t
<Instance of x, address 7797696: name b=2 name a=1 name c=3 >
Mix-in classes are a powerful technique. In practice, multiple inheritance is an advanced tool and can become complicated if used carelessly or excessively. Like almost everything else in programming, it can be a useful device when applied well. We’ll revisit this topic as a gotcha at the end of this part of the book. In Chapter 23, we’ll also meet an option (new style classes) that modifies the search order for one special multiple inheritance case.
Because
classes
are objects, it’s easy to pass them around a
program, store them in data structures, and so on. You can also pass
classes to functions that generate arbitrary kinds of
objects;
such functions are sometimes called factories in
OOP design circles. They are a major undertaking in a strongly typed
language such as C++, but almost trivial in Python: the
apply
function and syntax we met in Chapter 14 can call any class with any number of
constructor arguments in one step, to generate any sort of
instance:[3]
def factory(aClass, *args): # varargs tuple return apply(aClass, args) # Call aClass. class Spam: def doit(self, message): print message class Person: def __init__(self, name, job): self.name = name self.job = job object1 = factory(Spam) # Make a Spam. object2 = factory(Person, "Guido", "guru") # Make a Person.
In this code, we define an object generator function, called
factory
. It expects to be passed a class object
(any class will do), along with one or more arguments for the
class’s constructor. The function uses
apply
to call the function and return an instance.
The rest of the example simply defines two classes and generates
instances of both by passing them to the factory
function. And that’s the only factory function you
ever need write in Python; it works for any class and any constructor
arguments. One possible improvement worth noting: to support keyword
arguments in constructor calls, the factory can collect them with a
**args
argument and pass them as a third argument
to apply
:
def factory(aClass, *args, **kwargs): # +kwargs dict return apply(aClass, args, kwargs) # Call aClass.
By now, you should know that everything is an “object” in Python; even things like classes, which are just compiler input in languages like C++. However, as mentioned at the start of Part VI, only objects derived from classes are OOP objects in Python.
So what good is the factory
function (besides
giving us an excuse to illustrate class objects in this book)?
Unfortunately, it’s difficult to show you
applications of this design pattern, without listing much more code
than we have space for here. In general, though, such a factory might
allow code to be insulated from the details of dyamically-configured
object construction.
For instance, recall the processor
example
presented in the abstract in Chapter 19, and then
again as a has-a composition example in this chapter. It accepted
reader and writer objects for processing arbitrary data streams. The
original version of this example manually passed in instances of
specialized classes like FileWriter
and
SocketReader
to customize the data streams being
processed; later, we passed in hardcoded file, stream, and formatter
objects. In a more dynamic scenario, streams might be configured by
external devices such as configuration files or GUIs.
In such a dynamic world, we might not be able to hardcode the creation of stream interface objects in our script, but might instead create them at runtime according to the contents of a configuration file. For instance, the file might simply give the string name of a stream class to be imported from a module, plus an optional constructor call argument. Factory-style functions or code may come in handy here, because we can fetch and pass in classes that are not hardcoded in our program ahead of time. Indeed, those classes might not even have existed at all when we wrote our code:
classname = ...parse from config file... classarg = ...parse from config file... import streamtypes # Customizable code aclass = getattr(streamtypes, classname) # Fetch from module reader = factory(aclass, classarg) # or aclass(classarg). processor(reader, ...)
Here, the getattr
built-in is used to fetch a
module attribute given a string name again (it’s
like saying obj.attr
, but attr
is a string). Because this code snippet assumes a single constructor
argument, it doesn’t strictly need either
factory
or apply
(we could make
an instance with just aclass(classarg)
); they may
prove more useful in the presence of unknown argument lists. The
general factory coding pattern, though, can improve code flexibility.
For more details on such things, please consult books that cover OOP
design and design patterns.
Methods are a kind of object, much like functions. Class methods can be accessed from either an instance or a class; because of this, they actually come in two flavors in Python:
self
Accessing a class’s function attribute by qualifying a class returns an unbound method object. To call it, you must provide an instance object explicitly as its first argument.
self
+ function pairsAccessing a class’s function attribute by qualifying an instance returns a bound method object. Python automatically packages the instance with the function in the bound method object, so we don’t need to pass an instance to call the method.
Both kinds of methods are full-fledged objects; they can be passed
around, stored in lists, and so on. Both also require an instance in
their first argument when run (i.e., a value for
self
). This is why we’ve had to
pass in an instance explictly when calling superclass methods from
subclass methods in the previous chapter; technically, such calls
produce unbound method objects.
When calling a bound method object, Python provides an instance for you automatically—the instance used to create the bound method object. This means that bound method objects are usually interchangeable with simple function objects, and makes them especially useful for interfaces written originally for functions (see the sidebar on callbacks for a realistic example).
To illustrate, suppose we define the following class:
class Spam: def doit(self, message): print message
Now, in normal operation, we make an instance, and call its method in a single step to print the passed argument:
object1 = Spam( ) object1.doit('hello world')
Really, though, a bound method object is
generated along the way—just before the method
call’s parenthesis. In fact, we can fetch a bound
method without actually calling it. An object.name
qualification is an object expression. In the following, it returns a
bound method object that packages the instance
(object1
) with the method function
(Spam.doit
). We can assign the bound method to
another name, and call it as though it were a simple function:
object1 = Spam( ) x = object1.doit # Bound method object: instance+function x('hello world') # Same effect as object1.doit('...')
On the other hand, if we qualify the class to get to
doit
, we get back an unbound
method object, which is simply a reference to the function object. To
call this type of method, pass in an instance in the leftmost
argument:
object1 = Spam( ) t = Spam.doit # Unbound method object t(object1, 'howdy') # Pass in instance.
By extension, the same rules apply within a class’s
method if we reference self
attributes that refer
to functions in the class. A self.method
is a
bound method object, because self
is an instance
object:
class Eggs: def m1(self, n): print n def m2(self): x = self.m1 # Another bound method object x(42) # Looks like a simple function Eggs( ).m2( ) # Prints 42
Most of the time, you call methods immediately after fetching them with qualification, so you don’t always notice the method objects generated along the way. But if you start writing code that calls objects generically, you need to be careful to treat unbound methods specially—they normally require an explicit instance object to be passed in.[4]
Chapter 11 covered docstrings in detail in our look
at documentation sources and tools. Docstrings are string literals
that show up at the top of various structures, and are saved by
Python automatically
in
object __doc__
attributes. This works for module
files, function defs, and classes and methods. Now that we know more
about classes and methods, file docstr.py
provides a quick but comprehensive example that summarizes the places
where docstrings can show up in your code; all can be triple-quoted
blocks:
"I am: docstr.__doc__" class spam: "I am: spam.__doc__ or docstr.spam.__doc__" def method(self, arg): "I am: spam.method.__doc__ or self.method.__doc__" pass def func(args): "I am: docstr.func.__doc__" pass
The main advantage of documentation strings is that they stick around at runtime; if it’s been coded as a documentation string, you can qualify an object to fetch its documentation.
>>>import docstr
>>>docstr.__doc__
'I am: docstr.__doc__' >>>docstr.spam.__doc__
'I am: spam.__doc__ or docstr.spam.__doc__' >>>docstr.spam.method.__doc__
'I am: spam.method.__doc__ or self.method.__doc__' >>>docstr.func.__doc__
'I am: docstr.func.__doc__'
The discussion of the PyDoc tool that knows how
to format all these strings in reports appears in Chapter 11. Documentation strings are available at
runtime, but they are also less flexible syntactically than
#
comments (which can appear anywhere in a
program). Both forms are useful tools,
and
any program documentation is good (as long as it’s
accurate).
Finally, let’s wrap up this chapter by comparing the topics of this book’s last two parts—modules and classes. Since they’re both about namespaces, the distinction can sometimes be confusing. In short:
Are data/logic packages
Are created by writing Python files or C extensions
Are used by being imported
Implement new objects
Are created by class statements
Are used by being called
Always live within a module
Classes also support extra features modules don’t, such as operator overloading, multiple instance generation, and inheritance. Although both are namespaces, we hope you can tell by now that they are very different things.
[1] For another example of composition at work, see this part’s “Dead Parrot Sketch” exercise and solution; it’s similar to the Pizza shop example.
[2] If you’re
curious how, flip back to Seciton 21.5.4 for hints. We saw there that classes have a
built-in attribute called __bases__
, which is a
tuple of the class’s superclass objects. A
general-purpose class hierarchy lister or browser can traverse from
an instance’s __class__
to its
class, and then from the class’s __bases__
to all superclasses recursively, much like the
classtree.py
example shown earlier. In Python 2.2
and later it may be even simpler: the built-in dir
function now includes inherited attribute names automatically. If you
don’t care about displaying tree structure, you
might just scan the dir
list instead of the
dictionary keys list, and use getattr
to fetch
attributes by name string instead of dictionary key indexing.
We’ll rehash this idea in an exercise and its
solution.
[3] Actually, apply
can call
any callable object; that includes functions, classes, and methods.
The factory
function here can run any callable,
not just a class (despite the argument name).
[4] See the upcoming discussion of the static and class methods extension in Python 2.2, for an optional exception to this rule. Like bound methods, both of these can masquerade as basic functions too, because they do not expect an instance when called.