ERROR ON PREV
How to Program, Part VI
  1. How to Program, Part I
  2. How to Program, Part II
  3. How to Program, Part III
  4. How to Program, Part IV
  5. How to Program, Part V
  6. How to Program, Part VI
  7. exercises
  8. pyMPI tutorial
  9. Calculating PI, Part I
  10. Calculating PI, Part II
  11. Calculating PI, Part III
  12. Dividing Work
  13. More MPI
  14. Poogle - Web Search
  15. Mandelbrot Sets
  16. Mandelbrot, The Code
  17. Mandelbrot, The Images
  18. Mandelbrot In CUDA
  19. Conway's Life, Part I
  20. Life Code Listing
  21. Conway's Life, Part II
  22. MPI Life Code Listing

How to Program, Part VI

Object oriented programming with Python. Here is the way we define and instantiate a simple object:

object1.py
1class MyObject(object):
2  "Documentation string for MyObject"
3  pass
4 
5my = MyObject()
6help(my)
$ python ./object1.py
Help on MyObject in module __main__ object:

class MyObject(__builtin__.object)
 |  Documentation string for MyObject
 |  
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

Because we wish to be good coders, we provided a documentation string. You can print the documentation for the class using the "help()" function.

Note also the "(object)" in parenthesis after the class name. While most of the code in this class will work without putting that there, we will always do so in this class. It marks the object as a "new style" Python object.

If you wish to provide any special initialization for your object, you do it with a constructor (the __init__ method). Python has many special methods tha begin and end with double underscore (they are called "magic names").

object2.py
1class MyObject(object):
2  """
3  This is a multi-line
4  documentation string.
5  """
6  def __init__(self):
7    pass
8 
9my = MyObject()
10help(my)
$ python ./object2.py
Help on MyObject in module __main__ object:

class MyObject(__builtin__.object)
 |  This is a multi-line
 |  documentation string.
 |  
 |  Methods defined here:
 |  
 |  __init__(self)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

All object methods must have at least one argument which, by convention, is called "self." If you're a C++ or Java person, you might feel more comfortable with "this" instead.

object3.py
1class MyObject(object):
2  def __init__(self,a,b):
3    self.a = a
4    self.b = b
5  def __str__(self):
6    return "("+str(self.a)+","+str(self.b)+")"
7 
8my = MyObject(3,4)
9print my
$ python ./object3.py
(3,4)

The definition of our MyObject is called the class. It's like a blueprint for making objects of type MyObject. On line 8 above we create an instance of that class. A specific example.

When we use self to set a field on our class, we are setting it on the instance. We can set it on the class as well. In this next example, the field "c" is set on the class instead of the instance. That means that all instances of MyObject see the same value for "c".

object3-1.py
1class MyObject(object):
2  c = 5
3  def __init__(self,a,b):
4    self.a = a
5    self.b = b
6  def __str__(self):
7    return "("+str(self.a)+","+str(self.b)+")"
8 
9print MyObject.c
10my = MyObject(3,4)
11print my
$ python ./object3-1.py
5
(3,4)

In some cases, you may wish to hide a variable from users of your class. To do this in python, simply prepend two underscores to the front of your name.


Private Data:

private.py
1class MyClass(object):
2  def __init__(self):
3    self.__priv = 7
4  def __str__(self):
5    return "("+str(self.__priv)+")"
6m = MyClass()
7print m
8print m.__priv # error happens here
$ python ./private.py
(7)
Traceback (most recent call last):
  File "./private.py", line 8, in <module>
    print m.__priv # error happens here
AttributeError: 'MyClass' object has no attribute '__priv'

Unlike other languages, the enforcement of private data in Python is weak. You can easily get around it by prepending the class name and an underscore.

private2.py
1class MyClass(object):
2  def __init__(self):
3    self.__priv = 7
4  def __str__(self):
5    return "("+str(self.__priv)+")"
6m = MyClass()
7print m
8print m._MyClass__priv
$ python ./private2.py
(7)
7

Destructors: Python offers you the ability to automatically clean up your objects (in some cases). You can do this with a destructor.

dtors.py
1class MyClass(object):
2  def __init__(self,a):
3    self.a = a
4    print "instantiating "+str(self.a)
5  def __str__(self):
6    return "("+str(self.a)+")"
7  def __del__(self):
8    print "destructing "+str(self.a)
9m1 = MyClass(1)
10m2 = MyClass(2)
11m3 = MyClass(3)
12m2.f = m3
13m3.f = m2 # create a cycle of references
14print m2.f,m2.f.f,m2.f.f.f
$ python ./dtors.py
instantiating 1
instantiating 2
instantiating 3
(3) (2) (3)
destructing 1

Static and class methods: Just like Java or C++, Python allows you to declare static methods--these are methods that don't require any instance of the class to exist.

You can also declare a "class method." These are similar to static methods, but take the class as an argument.

static.py
1class MyClass(object):
2  f = 5
3  @staticmethod
4  def do_it():
5    print "Do something"
6  @classmethod
7  def do_it2(cl):
8    print "f="+str(cl.f)
9 
10MyClass.do_it()
11MyClass.do_it2()
$ python ./static.py
Do something
f=5

Introspection: Python detects when object m1 goes out of scope and calls the destructor. Objects m2 and m3 are part of a cycle, and Python is not able to figure out when to call a destructor.

There are several functions you can use to learn about an object (or "introspect" it), or change it. The "dir()" function returns the names of all the attributes (member variables and field) of something. The "getattr()" function lets you look up the value of an attribute on an object by name. Don't worry what all of the output means right now. Note, however, the "__class__" attribute. This refers to the special object that contains the data for the class (or blueprint).

object3-2.py
1class MyObject(object):
2  c = 5
3  def __init__(self,a,b):
4    self.a = a
5    self.b = b
6  def __str__(self):
7    return "("+str(self.a)+","+str(self.b)+")"
8 
9print MyObject.c
10my = MyObject(3,4)
11for attr in dir(my):
12  print attr,"=",getattr(my,attr)
$ python ./object3-2.py
5
__class__ = <class '__main__.MyObject'>
__delattr__ = <method-wrapper '__delattr__' of MyObject object at 0x7f88052a0fd0>
__dict__ = {'a': 3, 'b': 4}
__doc__ = None
__format__ = <built-in method __format__ of MyObject object at 0x7f88052a0fd0>
__getattribute__ = <method-wrapper '__getattribute__' of MyObject object at 0x7f88052a0fd0>
__hash__ = <method-wrapper '__hash__' of MyObject object at 0x7f88052a0fd0>
__init__ = <bound method MyObject.__init__ of <__main__.MyObject object at 0x7f88052a0fd0>>
__module__ = __main__
__new__ = <built-in method __new__ of type object at 0x3a8119adc0>
__reduce__ = <built-in method __reduce__ of MyObject object at 0x7f88052a0fd0>
__reduce_ex__ = <built-in method __reduce_ex__ of MyObject object at 0x7f88052a0fd0>
__repr__ = <method-wrapper '__repr__' of MyObject object at 0x7f88052a0fd0>
__setattr__ = <method-wrapper '__setattr__' of MyObject object at 0x7f88052a0fd0>
__sizeof__ = <built-in method __sizeof__ of MyObject object at 0x7f88052a0fd0>
__str__ = <bound method MyObject.__str__ of <__main__.MyObject object at 0x7f88052a0fd0>>
__subclasshook__ = <built-in method __subclasshook__ of type object at 0x1b5b7f0>
__weakref__ = None
a = 3
b = 4
c = 5

There is also the "hasattr()" function, to test whether an object has an attribute, "setattr()" to change the attribute, and "delattr()" to remove it.

performing [python ./object3-3.py]
object3-3.py
1class MyObject(object):
2  c = 5
3  def __init__(self,a,b):
4    self.a = a
5    self.b = b
6  def __str__(self):
7    return "("+str(self.a)+","+str(self.b)+")"
8 
9my = MyObject(3,4)
10print hasattr(my,"a"),hasattr(my,"d")
11print getattr(my,"a")
12delattr(my,"a")
13print hasattr(my,"a")
$ python ./object3-3.py
0
0.5
0.5
0.5
Traceback (most recent call last):
  File "/usr/lib/python2.7/site.py", line 68, in <module>
    import os
  File "/usr/lib/python2.7/os.py", line 400, in <module>
    import UserDict
  File "/usr/lib/python2.7/UserDict.py", line 116, in <module>
    import _abcoll
  File "/usr/lib/python2.7/_abcoll.py", line 70, in <module>
    Iterable.register(str)
  File "/usr/lib/python2.7/abc.py", line 107, in register
    if not isinstance(subclass, (type, types.ClassType)):
AttributeError: 'module' object has no attribute 'ClassType'

This code works for the instance variable "a", but for the class variable "c" we sometimes need to use the class definition.

performing [python ./object3-3.py]
object3-3.py
1 
$ python ./object3-3.py
0
0.5
0.5
0.5
Traceback (most recent call last):
  File "/usr/lib/python2.7/site.py", line 68, in <module>
    import os
  File "/usr/lib/python2.7/os.py", line 400, in <module>
    import UserDict
  File "/usr/lib/python2.7/UserDict.py", line 116, in <module>
    import _abcoll
  File "/usr/lib/python2.7/_abcoll.py", line 70, in <module>
    Iterable.register(str)
  File "/usr/lib/python2.7/abc.py", line 107, in register
    if not isinstance(subclass, (type, types.ClassType)):
AttributeError: 'module' object has no attribute 'ClassType'

Now for a powerful insight about the nature of Python: Everything is an object. What does that mean?

everything.py
1class MyObject(object):
2  pass
3my = MyObject()
4print hasattr(my,'__class__'),getattr(my,'__class__')
5print hasattr(3,'__class__'),getattr(3,'__class__')
6print hasattr("x",'__class__'),getattr("x",'__class__')
$ python ./everything.py
True <class '__main__.MyObject'>
True <type 'int'>
True <type 'str'>

There are a number of special methods that you can define. We've already seen __str__, __init__, and __del__, but one can also define operators such as +, -, and **.

object4.py
1class MyObject:
2  def __init__(self,a,b):
3    self.a = a
4    self.b = b
5  def __add__(self,m):
6    return MyObject(self.a+m.a,self.b+m.b)
7  def __str__(self):
8    return "("+str(self.a)+","+str(self.b)+")"
9 
10my1 = MyObject(1,2)
11my2 = MyObject(3,4)
12my3 = my1 + my2
13print "%s + %s = %s" % (my1,my2,my3)
14my4 = MyObject.__add__(my1,my2) # another way to call it
15print "%s + %s = %s" % (my1,my2,my4)
$ python ./object4.py
(1,2) + (3,4) = (4,6)
(1,2) + (3,4) = (4,6)

You can find more operators here.


Inheritance: In this next example, we show how one class can inherit from another. Object Obj2 is just like Obj1, except some things are added. If you define a new constructor, you should call the constructor of the class you inherit from to make sure the base object is properly initialized.

object4-1.py
1class Obj1(object):
2  def __init__(self,a):
3    self.a = a
4class Obj2(Obj1):
5  def __init__(self,a,b):
6    Obj1.__init__(self,a) # construct superclass
7    self.b = b
8class Obj3(Obj1):
9  def __init__(self,a,b):        # another way to
10    super(Obj3,self).__init__(a) # construct superclass
11    self.b = b
12 
13x = Obj2(1,2)
14print x.a,x.b
15y = Obj3(8,9)
16print y.a,y.b
$ python ./object4-1.py
1 2
8 9

If you don't provide an __init__ method in your base class, the method from the superclass will be inherited.

object4-2.py
1class Obj1(object):
2  def __init__(self,a):
3    self.a = a
4class Obj2(Obj1):
5  pass
6 
7x = Obj2(14)
8print x.a
$ python ./object4-2.py
14

You can inherit from more than one class.

object4-3.py
1class Obj1(object):
2  def __init__(self,a):
3    self.a = a
4class Obj2(object):
5  def __init__(self,b):
6    self.b = b
7class Obj3(Obj1,Obj2):
8  def __init__(self,a,b):
9    Obj1.__init__(self,a)
10    Obj2.__init__(self,b)
11o = Obj3(9,10)
12print o.a,o.b
$ python ./object4-3.py
9 10

Factoid: You can distinguish types in Python by calling the type() function.

object4-4.py
1class MyClass(object):
2  pass
3print type(1),type({}),type([]),type("x"),type(MyClass())
$ python ./object4-4.py
<type 'int'> <type 'dict'> <type 'list'> <type 'str'> <class '__main__.MyClass'>

In addition, you can use the "isinstance()" method to see if a class is an instance of a given class. In the example, MyOtherClass inherits from MyClass, so it is an instance of MyClass.


Fun and tricks: One of the neat features of Python is the decorator. These are words beginning with the @ symbol, and are used modify the functionality of a class or function.

performing [python ./object4-5.py]
object4-5.py
1 
$ python ./object4-5.py
0
0.5
0.5
0.5
Traceback (most recent call last):
  File "/usr/lib/python2.7/site.py", line 68, in <module>
    import os
  File "/usr/lib/python2.7/os.py", line 400, in <module>
    import UserDict
  File "/usr/lib/python2.7/UserDict.py", line 116, in <module>
    import _abcoll
  File "/usr/lib/python2.7/_abcoll.py", line 70, in <module>
    Iterable.register(str)
  File "/usr/lib/python2.7/abc.py", line 107, in register
    if not isinstance(subclass, (type, types.ClassType)):
AttributeError: 'module' object has no attribute 'ClassType'
performing [python ./object4-5.py]
object4-5.py
1 
$ python ./object4-5.py
0
0.5
0.5
0.5
Traceback (most recent call last):
  File "/usr/lib/python2.7/site.py", line 68, in <module>
    import os
  File "/usr/lib/python2.7/os.py", line 400, in <module>
    import UserDict
  File "/usr/lib/python2.7/UserDict.py", line 116, in <module>
    import _abcoll
  File "/usr/lib/python2.7/_abcoll.py", line 70, in <module>
    Iterable.register(str)
  File "/usr/lib/python2.7/abc.py", line 107, in register
    if not isinstance(subclass, (type, types.ClassType)):
AttributeError: 'module' object has no attribute 'ClassType'

In the above example, Python saw the definition for <, and ==, and created methods for all the other comparison operators. We didn't define the greater >= operator, but it's there. All we had to do was use the total_ordering decorator.

For your interest, here's how to create a decorator for an object. In this example, our decorator provides the method "pr".

object5.py
1# Pass a class in
2def add_pr(clazz):
3  # Create a new class from the one passed in
4  class newclazz(clazz):
5    # Add a method to this class
6    def pr(self):
7      print "this is pr"
8  # Return the new class, or modified blueprint
9  return newclazz
10 
11@add_pr
12class MyClass(object):
13  pass
14 
15m = MyClass()
16m.pr()
$ python ./object5.py
this is pr

Here's how to create a total ordering with only a < function.

object6.py
1def ordering(clazz):
2  class newclazz(clazz):
3    def __ge__(self,m):
4      return m < self;
5    def __ne__(self,m):
6      return (m < self) or (self < m)
7    def __eq__(self,m):
8      return not (self != m)
9    def __le__(self,m):
10      return self < m or self == m
11    def __gt__(self,m):
12      return not (self <= m)
13  return newclazz
14 
15@ordering
16class Num(object):
17  def __init__(self,n):
18    self.n = n
19  def __str__(self):
20    return str(self.n)
21  def __lt__(self,num):
22    return self.n < num.n
23 
24n1 = Num(1)
25for i in range(3):
26  n2 = Num(i)
27  print "%s  < %s = %s" % (n1,n2,n1 < n2)
28  print "%s <= %s = %s" % (n1,n2,n1 <= n2)
29  print "%s  > %s = %s" % (n1,n2,n1 > n2)
30  print "%s >= %s = %s" % (n1,n2,n1 >= n2)
31  print "%s == %s = %s" % (n1,n2,n1 == n2)
32  print "%s != %s = %s" % (n1,n2,n1 != n2)
$ python ./object6.py
1  < 0 = False
1 <= 0 = False
1  > 0 = True
1 >= 0 = True
1 == 0 = False
1 != 0 = True
1  < 1 = False
1 <= 1 = True
1  > 1 = False
1 >= 1 = False
1 == 1 = True
1 != 1 = False
1  < 2 = True
1 <= 2 = True
1  > 2 = False
1 >= 2 = False
1 == 2 = False
1 != 2 = True

OK, that was a bit of a digression into a slightly obscure bit of python, but I hope you think it was fun! What should this code do?

object7.py
1class Num(object):
2  def __init__(self,n):
3    self.n = n
4  def __lt__(self,num):
5    return self.n < num.n
6  def __add__(self,m):
7    return Num(self.n + m.n)
8  def __str__(self):
9    return str(self.n)
10  def __mul__(self,n):
11    if type(n) == int:
12      if n == 0:
13        return 0
14      sum = self
15      n -= 1
16      while n > 0:
17        sum += self
18        n -= 1
19      return sum
20    elif isinstance(n,Num):
21      return self * n.n
22 
23n1 = Num(1)
24n2 = Num(2)
25print n1*30*n2

Properties are another cool trick. You can turn accesses of an object's member fields into function calls.

Props.py
1class Scramjet(object):
2  def __init__(self):
3    self.__engine = "Acme"
4 
5  @property
6  def engine(self):
7    print "Getting the engine: "+self.__engine
8    return self.__engine
9 
10  @engine.setter
11  def engine(self,value):
12    print "Setting the engine: "+value
13    self.__engine = value
14    return value
15s = Scramjet()
16e = s.engine
17print "Engine is",e
18e = s.engine = "Boeing"
19print "Engine is",e
$ python ./Props.py
Getting the engine: Acme
Engine is Acme
Setting the engine: Boeing
Engine is Boeing