Inheritance

Giving one class the same methods as another is one way to make them polymorphic, but it suffers from the same flaw as initializing an object's instance variables from outside the object. If a programmer forgets just one line of code, the whole program can fail for reasons that will be difficult to track down. A better approach is to use a third fundamental feature of object-oriented programming called inheritance, which allows you to recycle code in yet another way.

To explore how this is done, suppose we are trying to simulate the ecosystem of a tide pool. The pool contains all sorts of living things that float around and interact with their neighbors. Each critter moves around in its own special way and has its own favorite food. We define a class Organism that represents this living thing:

Download oop/organism.py

class Organism(object):

'''A living thing that is at location (x,y) in the tide pool.'''

self.name = name self.x = x self.y = y def __str__(self):

'''Return a string representation of this Organism.'''

return '(%s, [%s, %s])' % (self.name, self.x, self.y)

def can_eat(self, food):

'''Report whether this Organism can eat the given food. Since we don't know anything about what a generic organism eats, this always returns False.'''

return False def move(self):

'''Ask the organism to move. By default, this does nothing, since we don't know anything about how fast or how far a generic organism would move.'''

return

The class Organism has three instance variables: name, x, and y (the latter two representing its coordinates within the pool). It also has four methods: __init__ , __str__ , can_eat, and move. since we don't know any thing about the Organism, it never actually moves and doesn't eat anything.

Not all real organisms behave like our generic Organism. Crabs walk, green algae float, and mussels swim—though not the same way fish do. Some eat plants; some eat animals; green algae "eat" sunlight through photosynthesis.

We can define these specific types of organisms as new classes. Rather than defining each one from scratch, we start with our generic organism by replacing object with Organism in the class header:

Download oop/arthropod_header.py

class Arthropod(Organism): pass

Here, the keyword pass means "do nothing to the definitions inherited from Organism." We call Organism the parent or superclass, and we call Arthropod the child or subclass. Arthropod inherits instance variables and methods from its parent Organism, so an Arthropod object automatically has the instance variables name, x, and y and the methods_init_,

Download oop/arthropod_basic.py

>>> blue_crab = Arthropod('Callinectes sapidus' , 0, 0) >>> print blue_crab (Callinectes sapidus, 0, 0)

However, we want the Arthropod class to be more than just a generic Organism, which means giving it its own instance variables, methods, or both. In this case, a leg_count instance variable is needed, which requires changing the definition of __init__ :

Download oop/arthropod_init.py

class Arthropod(Organism):

'''An arthropod that has a fixed number of legs.'''

'''An arthropod with the given number of legs that exists at location (x, y) in the tide pool.'''

self.legs = legs

In the first line of_init_, we call the constructor from class Organism to initialize the three instance variables, which are name, x, and y. The second line creates the leg_count instance variable and initializes it. Arthropod._init_ overrides Organism_init_, so when an instance of

Arthropod calls_init_, the version defined in that class is called, rather than the one in the parent class:

Download oop/arthropod_init_result.py

>>> lobster = Arthropod('Homarus gammarus' , 0, 0) Traceback (most recent call last): File "<stdin>", line 1, in <module>

TypeError: _init_() takes exactly 5 arguments (4 given)

>>> lobster = Arthropod('Homarus gammarus' , 0, 0, 10)

The call to the Arthropod constructor with only three arguments results in an error. Once a function is overridden, the parent's version of the function cannot be called from outside the class definition.

Notice, though, that when we print an instance of Arthropod, the number of legs isn't reported:

Download oop/arthropod_str.py

>>> lobster = Arthropod('Homarus gammarus' , 0, 0, 10) >>> print lobster (Homarus gammarus, 0, 0)

This happens because we haven't overridden the str method, so Organism._str_is still being called. Fixing this is straightforward:

Download oop/arthropod_str_override.py

class Arthropod(Organism):

'''An arthropod that has a fixed number of legs.'''

'''An arthropod with the given number of legs that exists at location (x, y) in the tide pool.'''

'''Return a string representation of this Arthropod.'''

return '(%s, %s, [%s, %s])' % (self.name, self.legs, self.x, self.y)

Of course, a child class can also have methods that are not part of the parent class:

Download oop/arthropod.py

class Arthropod(Organism):

'''An arthropod that has a fixed number of legs.'''

ftno ammonia.pdb - ammonia

File Edit Display View lools Macros

Help aes-c q. o a is ikií<t>i ammonia.pdb - ammonia

File Edit Display View lools Macros

Help

473x279

473x279

Figure 13.2: Jmol: an open source Java viewer for chemical structures def _init_(self, name, x, y, legs):

'''An arthropod with the given number of legs that exists at location (x, y) in the tide pool.''1

'''Return a string representation of this Arthropod.'''

return '(%s, %s, [%s, %s])' % (self.name, self.legs, self.x, self.y)

def is_decapod(self):

'''Return True if this Arthropod is a decapod.'''

return self.legs == 10

def leg_count(self):

'''Return the number of legs this Arthropod possesses.'''

return self.legs

13.5 A Longer Example

Molecular graphic visualization tools allow for interactive exploration of molecular structures. Most read PDB-formatted files, which we describe in Section 8.4, Multiline Records, on page 177.

in 3D

In a molecular visualizer, every atom, molecule, bond, and so on, has a location in 3D space, usually defined as a vector, which is an arrow from the origin to where the structure is. All of these structures can be rotated and translated.

A vector is usually represented by x, y, and z coordinates that specify how far along the x-axis, y-axis, and z-axis the vector extends.

Here is how ammonia can be specified in PDB format:

Download fileproc/ammonia.pdb

COMPND AMMONIA

In our simplified PDB format, a molecule is made up of numbered atoms. In addition to the number, an atom has a symbol and (x, y, z) coordinates. For example, in one of the atoms in ammonia is nitrogen with symbol N at coordinates (0.257, -0.363, 0.0). In the following sections, we will look at how we could translate these ideas into object-oriented Python.

Was this article helpful?

0 0

Post a comment