Class Inheritance and Composition

 

Creating new classes through composition

Now we�ll create a new class, UTM_Lineseg, which represents a straight line segment in 2D space

The segment will be bounded by objects of the class UTM coordinates

Composition means that some of our class member variables are objects of some other class

Note: if the �other class� is defined in a separate module, you must import that module and refer to it as module_name.class_name (or use the shorthand name introduced by the keyword �as�), e.g.

import UTM_Coord as uc # UTM_Coord is module name, uc is shorthand

class someClass:

def __init__(self):

# Since UTM_Coord is defined in another module,

# refer to it by its module name or shortcut name, as below

self.myCoord = uc.UTM_Coord(580000, 4720000, 18, �N�)

The code for creating SimpleUTM_Lineseg is here (Save as SimpleClassByComp.py in the same folder as UTM_Coord.py)

We build a UTM_Lineseg class through a very simple constructor that accepts 2 UTM_Coord objects

Although this class is ok if the user works carefully, it can break easily if the user supplies incorrect arguments

We should check whether the user of the class has supplied the correct arguments

The code for safely building a class through composition is here (save as UTM_Lineseg.py in the same folder as UTM_Coord.py)

A safe class fails �gracefully� (with error messages�not by crashing your program)

Our safer class will look like this:

import math # this class is referred to in length()

class UTM_Lineseg:

def __init__(self, p0, p1):

if not isinstance(p0, UTM_Coord): # if p0 is not

��������������������������������� # a UTM_Coord object�

print "p0 is not a UTM_Coord object" # stop doing stuff

self.p0 = None # create an empty object that can be tested for

self.p1 = None

return

if not isinstance(p1, UTM_Coord):

print "p1 is not a UTM_Coord object"

self.p0 = None

self.p1 = None

return

# if we got here, p0 and p1 are OK

self.p0 = p0

self.p1 = p1

def length(self):

if self.p0 == None or self.p1 == None: # bad arguments

print "Invalid line segment--cannot compute length"

return -1

if self.p0.zone != self.p1.zone or self.p0.hemisphere != self.p1.hemisphere:

print "cannot find length of line crossing UTM zones or hemispheres"

return -1

������ # All is well. Calculate and return the length

deltaE = self.p0.easting - self.p1.easting

deltaN = self.p0.northing - self.p1.northing

deltaE_squared = deltaE * deltaE

deltaN_squared = deltaN * deltaN

return math.sqrt(deltaE_squared + deltaN_squared)

Classes that are built by composition use objects of classes just like other classes use built-in data types

Creating classes through inheritance

Inheritance lets you create a new class by extending, or inheriting from, and existing class

The code for building a class through inheritance is here (save as Road.py in the same folder as your other class files)

Let�s extend the UTM_Lineseg class to make a Road class

For now, the Road class will add just one more class member variable�a name for the road

Road inherits from UTM_Lineseg, meaning that it has all the characteristics of the inherited class (2 bounding UTM_Coord objects and a length() method) plus it has a name

We can refer to Road as a �child� class of UTM_Lineseg (the �parent� class):

 

import UTM_Lineseg as ul

 

class Road(ul.UTM_Lineseg): # Road inherits from UTM_Lineseg

 

def __init__(self, n, p0, p1):

# add name as a Road class member variable and assign n

self.name = n

# init class member variables of inherited class�������������

ul.UTM_Lineseg.__init__(self, p0, p1)

 

Most child classes will call the parent�s constructor:

ul.UTM_Lineseg.__init__(self, p0, p1)

We can now refer to the starting and ending nodes of a Road object as p0 and p1, just as if they were defined explicitly in the Road class

The following code in the Road class does just that:

 

def report(self):
TAB = '\t'
TAB2 = TAB + TAB
nameField = 'name: ' + TAB + self.name
p0CoordField = TAB2 + 'p0: easting: ' + str(self.p0.easting) + ', northing: ' + str(self.p0.northing)
p0UTMField = TAB2 + 'p0: UTM zone: ' + str(self.p0.zone) + self.p0.hemisphere
p1CoordField = TAB2 + 'p1: easting: ' + str(self.p1.easting) + ', northing: ' + str(self.p1.northing)
p1UTMField = TAB2 + 'p1: UTM zone: ' + str(self.p1.zone) + self.p1.hemisphere
lengthField = TAB2 + 'length: ' + str(self.length()) + ' meters'
print nameField
print p0CoordField
print p0UTMField
print p1CoordField
print p1UTMField
print lengthField

 

Code for testing out the Road class is here (save as BuildingRoads.py with your other classes)

Try building a new class, Political_Border, that also inherits from UTM_Lineseg

Add some class member variables and methods that are useful for dealing with borders

 

Here is a link to a set of related geometric and topological classes to play with