10 minute read

This Python tutorial covers Python modules, classes and object-oriented programming principles. This includes:

  • Modules: The Big Picture
  • Module Import Syntax
  • Packages
  • Inheritance
  • Classes
  • Exceptions

 

Modules

In Python a Module is the highest-level program organization unit, modules correspond to Python program files. Each file is a module, and modules import other modules to use the names they define. There are 3 important statements in Python that are used to process modules:

import – Lets a client fetch a module as a whole

from – Allows clients to fetch particular names from a module

reload – Provides a way to reload a module’s code without stopping Python

Modules are an easy way to organize components into a system by serving as self-contained packages of variables known as namespaces. All the names defined at the top level of a module file become attributes of the imported module object.

Import

Let’s assume I built a module called function.py and inside this module I defined a function called addition, like so:

'''
Created on Feb 10, 2010
@author: tims
'''

def addition(x,y):
    return x + y

Using the import statement I can use this function anywhere on my Pythonpath, even the interpreter.

>>> import function
>>> function.addition(3,4)
7

From

Notice that when you use the import function, you get all top level variables, functions, and classes. So when using an object you have to preface it with the module it comes from to avoid name space collisions. i.e. function.addition(). If you do not want to preface every object you call from the imported module you can use the from statement.

>>> from function import addition
>>> addition(5,5)
10

This is also useful if you don’t want all the objects from a specific module but maybe just 1 or 2.

Reload

The reload() built-in function is useful only if the original module is changed after it has been imported. Because modules are only imported once, any changes that occur after the import will not be shown unless a reload() is done.

Let’s say I take my function.py module and add the following code:

message = "First version"

def printer():
    print message

I then import function.py into the interpreter and run the function printer.

>>> import function
>>> function.printer()
First version

I then go back and change the variable message to say “Second Version”.

message = "Second version"

def printer():
    print message

But when I run printer() in the interpreter again it still says “First version”

>>> function.printer()
First version

Note: I could even run the import statement again and the message would not change.

>>> import function
>>> function.printer()
First version

Now, if I reload() the module, the change should go into effect.

>>> reload(function)
<module 'function' from 'C:\Python\cola\latms\pythonTraining\function.py'>

>>> function.printer()
Second version

 

Packages

In Python, a directory of Python code is said to be a package. You can import a module from a different directory, or package, using the import statement and a path of names separated by periods.

>>> import django.template

As you can see, this will become very important when importing other Python libraries like Django, Sqlalchemy, or Flask in the future.

However, any directory that is going to be used as a Python package must contain a __init__.py file. This can contain Python code or simply be left blank, but creating a Python module named __init__.py in every Python package creates a hook that allows Python to know where to search for information.

 

Classes and Object-Oriented Programming

In this next section I will attempt to explain Object-Oriented Programming (or OOP) in a “big picture” context. For those unfamiliar with the subject I strongly encourage you to read “OOP: The Big Picture” in Learning Python.

In Python, everything we’ve done up to this point has been object-based; a variable name is assigned to an object, a function is an object and it accepts objects as arguments and returns an object as output, a for loop will iterate through the indexes of an object. However, to truly be object-oriented, our objects have to participate in something called “inheritance hierarchy”.

To support an inheritance hierarchy we use Python’s most fundamental object-oriented tool, the class. Classes are roughly packages, or groups, of functions (methods) that process built-in object types.

Classes and Instances

Classes
– Serve as instance factories. Their attributes provide behavior – data and functions – that is inherited by all the instances generated from them. They are the framework used to construct instances.

Instances
– Represent the concrete items in a program’s domain. Their attributes record data that varies per specific object.

For example, a Class called Person could generate Instances George and Sally.

The distinction between a Class and its Instances is important to understand so that we can talk about Inheritance.

Inheritance

inheritance

 

Object-Oriented Programming, in Python, is built upon the idea of inheritance. In this example, Instance1 of Person, George, has all of the attributes of his instance as well as all the classes above him. We can describe George as an object that is Alive, he can grow, die, and react to his environment, he is also a Person, which means he is self-aware, capable of complex communication and society, and his name is George, social security number 555-55-5555, and his birthday is May 23, 1932.

Another important aspect of inheritance is that each attribute can be overridden. In instance1 of Animal you will notice that the attribute “society” is set to “complex” which overrides the attribute “society” in the class Animal. When Python searches for an attribute, it will start with the instance that it is in and work its way up to the sub-class and then the super-classes. If there are multiple super-classes it will search from left to right.

Note: A Super Class is just a Class that is higher up in the inheritance hierarchy structure. A Sub Class is just a Class that is lower in the hierarchy structure.

Syntax

Now it is time to look at the syntax for creating classes in Python.

>>> class FirstClass:
...     def setdata(self, value):
...         self.data = value

...     def display(self):
...         print self.data

Notice that this class, FirstClass, has 2 functions inside of it, functions that are part of classes are called methods, they behave like any other function except they are defined inside of a Class.

Next we call the class and then assign it to a variable, “x” and “y” are now instances of the class FirstClass().

>>> x = FirstClass()
>>> x.setdata('King Arthur')

>>> y = FirstClass()
>>> y.setdata(3.14159)

We can now call the methods assigned to the class FirstClass, note that Python will FIRST look for the method, setdata, in the instance, but since neither “x” nor “y” have a method called setdata or a variable called data then it looks in the class.

>>> x.display()
King Arthur

>>> y.display()
3.14159

Now we can call the display() method to display the variable data, which we set with the previous method, setdata.

To show that an instance really is its own, concrete object we can change the variable data, without using any of the methods available in the class.

>>> x.data = "New Value that is not King Arthur"
>>> x.display()
New Value that is not King Arthur

Now we can create a second class that is a sub class to FirstClass.

>>> class SecondClass(FirstClass):
...     def __init__(self):
...         self.data = 'Jack-of-all-trades'

Notice that we are assigning FirstClass as the super class by putting it in the parenthesis of SecondClass. If you have multiple super classes the order in which you list them in the parenthesis is very important. Python will always search for attributes in super classes starting with the super class on the left and moving to the right.

Also notice that the only method we created in SecondClass is called __init__, you will find this method quite commonly within class definitions in Python. It allows you to do many things whenever the class is initialized. In this case we are setting a default value to self.data.

>>> z = SecondClass()
>>> z.display()
Jack-of-all-trades

We have now created an instance z and assigned it to SecondClass(), notice that z is pulling a default value for the variable data from SecondClass but using the display() method from FirstClass.

This is a very basic and simple example of object-oriented programming in Python but it is the basis for a very powerful system of object representation. For a deeper dive I recommend reading the chapters on Classes and Inheritance in Learning Python.

 

Exceptions

An Exception is an event that can modify the flow of control through a program; they are triggered automatically on errors, and can be triggered and intercepted by your code. There are 5 statements that we can use to process exceptions.

try/except

Catch and recover from exceptions raised by Python or you.

First we define a function that returns the index of object that is passed in as an argument.

>>> def fetcher(obj, index):
...     return obj[index]

Then we define a string and call our fetcher function

>>> x = 'spam'
>>> fetcher(x,3)
'm'

If our arguments our not within the bounds of the object’s index, we get an error

>>> fetcher (x,4)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<console>", line 2, in fetcher
IndexError: string index out of range

The “try/except” statement allows us to customize this error message. We can catch this error and customize it like so:

>>> try:
...     fetcher(x,4)
... except IndexError:
...     print 'got exception'

got exception

try/finally

Perform cleanup actions, whether exceptions occur or not.

First we put our try statement inside of another function called catcher.

>>> def catcher():
...     try:
...         fetcher(x,4)
...     except IndexError:
...         print 'got exception'
...     finally:
...         print 'executes finally regardless of exception or not'
...     print 'continues to execute program after try statement'

>>> catcher()
got exception
executes finally regardless of exception or not
continues to execute program after try statement

You will notice the finally statement will execute at the end of the try statement regardless of whether an exception was hit or not, in this way, it is similar to the else statement at the end of a loop. Also, the program will continue to execute after the try statement has ran as shown in the print statement “continues to execute program after try statement”.

try/else

Run if no exceptions raised.

>>> def catcher2():
...     try:
...         fetcher(x,3)
...     except IndexError:
...         print 'got exception'
...     else:
...         print 'no exception'
...     finally:
...         print 'executes finally regardless of exception or not'
...     print 'continues to execute program after try statement'

>>> catcher2()
no exception
executes finally regardless of exception or not
continues to execute program after try statement

The else statement is used here in its more traditional statement, to perform an action if the exception does not get tripped.

assert

Conditionally trigger an exception in your code.

>>> x = 'spam'
>>> def f(x):
...     assert x != 'spam', 'x cannot be spam'

>>> f(x)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<console>", line 2, in f
AssertionError: x cannot be spam

>>> try:
...     f(x)
... except:
...     print 'x cannot be spam'

x cannot be spam

raise

Trigger an exception manually in your code

>>> class MyBad: pass

>>> def stuff():
...     raise MyBad()

>>> try:
...     stuff()
... except MyBad:
...     print 'got it'

got it

 

Exercises

  1. Create a class called “Parent”, create the following attributes and give them the following default values using the __init__ method:
    1. name = ‘John Smith’
    2. eyeColor = ‘blue’
    3. hairColor = ‘brown’
    4. education = ‘Master’s Degree’

Then make a display() method that displays all of these attributes. Save this class to a file to create a module.

  1. Import your “Parent” class from your module using the from, import. Create a sub-class called “Child” with “Parent” as its super-class. Override the following attributes from “Parent” with these values.
    1. name = “Jeff Smith”
    2. education = ‘High School Diploma’

Do not include the eyeColor or hairColor attributes in the “Child” sub-class. Do not make a display() method.

Next generate an instance of the “Parent” class and an instance of the “Child” class
– Set the eyeColor and hairColor of the “Child” instance in the actual instance
(i.e. “instancename”.eyeColor = “brown”, “instancename”.hairColor =”blonde”)
– Using the display() method, display all of their attributes.

 

Review

Go Back to Part 1 of this Python Tutorial: Types & Operations.

Go Back to Part 2 of this Python Tutorial: Statements, Syntax & Functions.