COMP 130 Elements of Algorithms and Computation
Spring 2012

Classes in Python

This is a very brief discussion of some selected topics about using classes in Python.   This is NOT a complete coverage of the topic!  

For more complete and official information on classes in Python, see the Python documentation: http://docs.python.org/tutorial/classes.html

A class is just a recipe for a closure over a collection of functions ("methods") and a collection of data ("fields" or "attributes").

An object is an instance of a closure with its associated methods and attributes, as defined by a class.

 

Basic syntax:

class MyClassName:

    def __init__(self, param1, param2, etc...):   
        """ Constructor for the class with zero or more input parameters: param1, param2, etc.
        The self parameter is required here, but not used when the constructor is called.
        """
        # create andintialize
        self.attribute1 = param1 
        self.attribute2 = param2
        etc.
        
    def method1(self, paramA, paramB):  
        """ A method with zero or more input parameters: param1, param2, etc.
        The self parameter is required here, but not used when the constructor is called.
        """
        # method code here
        # attributes referred to as self.attribute1, etc.
    

Note that all methods, including the constructor of a class must have "self" as their first input parameter.    When calling methods of an object or using the constructor, the "self" parameter is ignored as it is implicitly passed by the Python intepreter.

The above warning will cause Python interpreter to give errors stating that the number of required input parameters is one more than the code needs to supply.

Constructors

A constructor is a special method called "__init__"  that is used to initialize the an object instance when it is created from a class definition.   Most commonly, the constructor initializes the attributes of the object.

Usage:    aMyClassObject = MyClass(param1, param2)    # as many parameters as defined by __init__,  not including self.

 

Attributes

Python has the handy, but extremely dangerous feature that the first time an attribute is assigned a value, within the class definition code, by the syntax "self.xx = aValue"  where "xx" is the desired name of the attribute, the attribute is created and initialized.    Typically this is done in the constructor.

Usage:

Inside the class definition:  

self.xx = aValue

aValue = self.xx

Outside the class definition:  

myObject.xx = aValue

aValue = myObject.xx 

 By convention, any attrbute who's name is preceeded with one or two underscores is treated as a "private" attribute, not to be accessed from outside of the class code, i.e. from an object instance.   Using a preceeding double underscore also incurs some additional protections, so that is the recommended convention.

In general, the best practice is to treat all attributes as private and explicitly control access by defining "accessor" methods,  getXX() and setXX() to get and set the value of the XX attribute.

Methods

Methods of a class are defined the same as any function except that they

Usage:  

Inside the class definition:  

aResult = self.aMethod(aValue1, aValue2)   # self parameter is ignored

Outside the class definition:

aResult = myObject.aMethod(aValue1, aValue2)   # self parameter is ignored

 The same naming conventions as for attributes concerning "private" methods apply.

 

Special Methods

In general, these methods almost are never called directly by user code, but can be important in defining an object's behavior.

__eq__(self, other)

This method is used to determine equality between objects.   object1 == object2  is equivalent to object1.__eq(object2)

In general, even if two objects of the same class have the same values for their attributes, they are NOT equal because they are different instances.   The __eq__ method is used to override that behavior.

__hash__(self)

This function returns an integer "hash" value which is used by the Python system and in many data structures, such as dictionaries, to quickly identify an object.   Note that the hash value does NOT have to be unique for any given object, but it is better if it is.

However, if two objects are equal, as defined by __eq__, then their returned values from __hash__() MUST also be equal.    

This method must be defined if an object is to be used as a key in a dictionary.  Most dictionaries are implemented as hash tables.

For more information on hashing, see, for instance, the Wikipedia article on hash functions.  In addtion to its use here, hashing is a very important part of modern cryptography.

__str__(self)

This function defines the string representation of an object.    The returned string from this method is the result of called str(myObject).   

By default, if this method is not explicitly defined, the string representation of an object is a combination of its class name and its hash value.

Use this method to define a more "friendly" or informative string representation of the object instances of your classes.

Inheritance and Polymorphism

One of the key notions in any object-oriented system is that classes can be related to each other through an "inheritance" relationship.   Simply put, when one class, the "subclass", inherits from (is derived from) another class, the "superclass", the subclass is  automatically endowed with all the abilities (read: methods) of the superclass.    In addition, the subclass may also inherit the ability to access the internal data of the superclass -- this is always true in Python but not always true in most other languages.

But inheritance is much, much more than a simple labor-saving convenience.   It is a cornerstone of the abstract decomposition of a problem, a vital tool in expressing abstraction:

A superclass is an abstraction of all of its subclasses.

In particular, this means that if a piece of code is written such that it works with the superclass, any instances of its subclasses will also work in that same code.    This notion is called "polymorphism" -- th e ability of a subclass to be used anywhere its superclass can.

A  subclass is abstractly equivalent to its superclass.

An instance of a subclass is an instance of its superclass

Inheritance and polymorphism are just two sides of the same coin.   Inheritance looks at the subclass-superclass relationship from the subclass's point of view and polymorphism looks at the same relationship from the superclass's point of view.

Syntax and Usage Synopsis (see Python documentation for full details):


class MySuperClass:
    """"Superclass definition="""
     
    def __init__(self,param1):
        """Superclass constructor with parameters"""
        self._data1 = param1
 
    def method1(self, x):
        """A method of the superclass"""
        # legal to access self._data here
        
        
class MySubClassA(MySuperClass):       
    """ A subclass of MySuperClass"""
     
    def __init__(self, param1, param2):
        MySuperClass.__init__(self, param1)   # call superclass constructor to intialize 
        self._dataA = param2   # initialize data for this subclass
 
    def methodA(self, x):
        """ A method of this subclass"""
        # legal to access self._data here
        # Also legal to access self._dataA 
         
class MySubClassB(MySuperClass):       
    """ Another subclass of MySuperClass"""
     
    def __init__(self, param1, param2):
        MySuperClass.__init__(self, param1)   # call superclass constructor to intialize 
        self._dataB = param2   # initialize data for this subclass
 
    def methodB(self, x):
        """ A method of this subclass"""
        # legal to access self._data here
        # Also legal to access self._dataB
        # Cannot access self._dataA 
         
p1 = "p1"
p2 = 42
         
a = MySubClassA(p1, p2)   # new instance of MySubClassA
b = MySubClassB(p1, p2)   # new instance of MySubClassB
 
# Either a or b can be used anywhere that a MySuperClass object can be used.
p = 99
 
a.method1(p)   # legal call -- inherited from MySuperClass
b.method1(p)   # legal call -- inherited from MySuperClass
 
a.methodA(p)   # legal call -- method of MySubClassA
b.methodB(p)   # legal call -- method of MySubClassB
         
a.methodB(p)   # illegal call! -- NOT method of MySubClassA
b.methodA(p)   # illegal call! -- Not method of MySubClassB