Friday, November 15, 2013

Python Training Part 3 (of 4)

In my ongoing series on EVERY business person should learn a programming language... and that language should be Python...   this is part 3 of 4.   Like prior trainings, this should be done in order.  You should already have python 2.6 installed, and be able to create files and invoke idle from either the shell or the gui.

Today's lesson is on "classes"... classes are a very important concept in Python (and almost any programming language).   They enable you to write much more readable and useful code.... most importantly they enable you to build complex systems that 'build on top of each other' using either member variables of classes or inheritance or both.

A class can be thought of as simply a "container" for one or more "variables" and one or more "methods".
Those variables are called 'member variables' because they belong "in the class".
Those methods are called 'class methods' or sometimes called 'member functions' because they (almost always) act on or use the member variables of that class.

An object is an instantiation of a class... e.g. while a class "describes" what are it's member variables and member functions...   an object is one instance (e.g. one of potentially many) class objects.

Okay, enough mumbo-jumbo, let's get some hands-on!

1. First, create a file called location_classes.py somewhere in your library include path (C:\python26 works)

2. In that file, put the following very simple class:
class Location(object):
    #MemberVariables
    #x    #y    #z
    def __init__(self,x,y,z):
        self.x = x
        self.y = y
        self.z = z

# this is one of the most basic classes possible, it has 3member variables, x, y, z, and one member function called "__init__".   "__init__" is a special member function that is called when an instance is created.
Now, let's use it.

3. Next, from within idle, type the following commands

from location_classes import Location
l21 = Location()  #this fails!   In our __init__ we required x,y,z to be set!
l2 = Location(3,4,5)
l1 = Location(0,0,0)
print l2
print l1
print l1.x
print l2.z

#notice the "dot" notation to access your instance member variables (l2 is an instance of class Location)
#notice how l1 is seperate from l2, but both are the same class?
#cool right? :)
#you havn't seen the least of it!

4. Now, change your class code to be as follows:
import math
from datetime import datetime

class Location(object):
    #MemberVariables
    #x    #y    #z
    def __init__(self,x=0,y=0,z=0):
        self.x = x
        self.y = y
        self.z = z
     
    def __str__(self):
        return "X: %f, Y: %f, Z: %f" % (self.x,self.y,self.z)

    def distance(self,destination):
        xDiff = destination.x - self.x
        yDiff = destination.y - self.y
        zDiff = destination.z - self.z
        return math.sqrt(math.pow((xDiff),2) + math.pow((yDiff),2) + math.pow((zDiff),2))

#notice we've done 3 things.. we've set some 'default values' to the __init__ function, we've added 2 new member functions __str__ and distance.  __str__ is a special one that defines that classes string representation.   let's try it!

5. Close Idle, and restart it... (you have to do this, or your classes may not get reloaded correctly).  type the following into the new idle window

from location_classes import Location
l21 = Location()  #this succeeds now!!!
print l21
l2 = Location(3,4,5)
l1 = Location(0,0,0)
print l2
print l1
print l1.x
print l2.z

print l1.distance(l2)  #notice it did the distance math from l1 to l2!  cool right?

#play around with this...
l1.z=55
print l1.distance(l2)

#and try stuff like this
l21 = l2
print l21
print l2

6. Okay, now to learn about including a class into a class... this is NOT inheritance, it is instead, making a class a member variable of another class!   (We'll learn inheritance later).   For now, update your location_classes.py file to be as follows:

import math
from datetime import datetime

class Location(object):
    #MemberVariables
    #x    #y    #z
    def __init__(self,x=0,y=0,z=0):
        self.x = x
        self.y = y
        self.z = z
     
    def __str__(self):
        return "X: %f, Y: %f, Z: %f" % (self.x,self.y,self.z)

    def distance(self,destination):
        xDiff = destination.x - self.x
        yDiff = destination.y - self.y
        zDiff = destination.z - self.z
        return math.sqrt(math.pow((xDiff),2) + math.pow((yDiff),2) + math.pow((zDiff),2))

def vectorTo(origin,destination,speed):
    if speed==0:
        return origin
    dist = origin.distance(destination)
 
    dx = (destination.x - origin.x) / dist
    dy = (destination.y - origin.y) / dist
    dz = (destination.z - origin.z) / dist

    x = origin.x + dx * speed
    y = origin.y + dy * speed
    z = origin.z + dz * speed
 
    return Location( x, y, z )

#a boat is defined by its length, width, height, type-code and position and heading (point boat is moving towards)
class Boat(object):
    #Member Variables
    #length = 0    #width = 0    #height = 0    #boat_type = 0    #position = Location(0,0,0)    #heading = Location(0,0,0)

    def __init__(self,boat_type):  #makes boat_type required
        self.length = 0
        self.width = 0
        self.height = 0
        self.position = Location(0,0,0)
        self.heading = Location(0,0,0)
        self.boat_type = boat_type

    def move_towards_heading(self, speed):
        if (self.position.distance(self.heading) <= speed):
            print "Will arrive"
            self.position = self.heading
            return True
        else:
            #move towards goal
            self.position = vectorTo(self.position, self.heading, speed)
        return False

## Notice We've made a function (vectorTo) which is used in a new member function of class Boat... (move_towards_heading)...
### Notice that the Boat class has 2 member variables that are a Location type!  And that the move_towards_heading updates the position vectors of the Boat class!  Cool right?

7. Let's play with our new boat! :)  From python idle command line type:
from location_classes import Boat
ab = Boat(1)  #a boat of type 1
ab.heading = Location(5,5,5)
print ab.position
print ab.position.distance(ab.heading)
ab.move_towards_heading(1)
print ab.position
print ab.position.distance(ab.heading)

ab.move_towards_heading(speed = 1) #note that i said speed=1 here, but i didn't have to, I could have just said 1, but I wanted to show you how in a function call you can specify which parameters are what...

print ab.position
print ab.position.distance(ab.heading)

ab.move_towards_heading(1)
print ab.position
print ab.position.distance(ab.heading)

#can you see our boat moving?  I can!   It's moving!!!!

8. Okay, time to learn about inheritance!   We need TIME!  Time, I say, time! Make your location_classes.py file look like this:

import math
from datetime import datetime

class Location(object):
    def __init__(self,x=0,y=0,z=0):
        self.x = x
        self.y = y
        self.z = z
     
    def __str__(self):
        return "X: %f, Y: %f, Z: %f" % (self.x,self.y,self.z)

    def distance(self,destination):
        xDiff = destination.x - self.x
        yDiff = destination.y - self.y
        zDiff = destination.z - self.z
        return math.sqrt(math.pow((xDiff),2) + math.pow((yDiff),2) + math.pow((zDiff),2))

def vectorTo(origin,destination,speed):
    if speed==0:
        return origin
    dist = origin.distance(destination)
 
    dx = (destination.x - origin.x) / dist
    dy = (destination.y - origin.y) / dist
    dz = (destination.z - origin.z) / dist

    x = origin.x + dx * speed
    y = origin.y + dy * speed
    z = origin.z + dz * speed
 
    return Location( x, y, z )

#a boat is defined by its length, width, height, type-code and position and heading (point boat is moving towards)
class Boat(object):
    #Member Variables
    #length = 0    #width = 0    #height = 0    #boat_type = 0    #position = Location(0,0,0)    #heading = Location(0,0,0)

    def __init__(self,boat_type):  #makes boat_type required
        self.length = 0
        self.width = 0
        self.height = 0
        self.position = Location(0,0,0)
        self.heading = Location(0,0,0)
        self.boat_type = boat_type


    def move_towards_heading(self, speed):
        if (self.position.distance(self.heading) <= speed):
            print "will arrive"
            self.position = self.heading
        else:
            #move towards goal
            self.position = vectorTo(self.position, self.heading, speed)
        return self.position
 
class Location4d(Location):
    t = datetime.now()
    def __init__(self,x,y,z,t):
        super(Location4d, self).__init__(x,y,z)
        if t > 0:
            self.t = t

    def __str__(self):
        return "X: %f, Y: %f, Z: %f, T: %s" % (self.x,self.y,self.z,self.t)

## notice we added a new class Location4d, and it inherited Location!   up till now, we've been inheriting "object" which is actually a very base class that gives us ability to call super  ( we didn't have to do this, we could have called  class Location()  but then we wouldn't have had access to the super function, which can be really helpful. )

### Also notice that since we inherited from Location class, that we actually have an x, y, z already as member variables, and we added t  (time!).

### We've over-ridden the __str__ function (by having defined ou own) and added to the __init__ function (using super)

#### let's play with it.

9. In your command shell  (idle) type the following:

from location_classes import Location4d
l4 = Location4d(0,0,0,0)
print l4
l3 = Location4d(4,5,6,0)
l4.distance(l3)
l3.distance(l4)
#  notice we called the distance function?   how you say?  simple, we inherited not only the member variables, but also the member functions!   Cool right?

10. What else can you do with classes?
Turns out you can do a lot just with these few things I've showed you... but there is so much more too!  Creating classes of classes of classes, you could model the universe!
Most libraries you use will start with a class as well!  So knowing how to 'instantiate' a class instance is key  * you did this many times when you said l4=Location4d(0,0,0,0) for example!

So, here's your homework!!!

Create a new class called Rectangle...  make sure it inherits from Location or Location4d  (your choice... yes, deep inheritance is possible, as is multiple inheritance..e.g. a location and a location 4d... although that makes no sense here).

Now, write a member function to return the area of the rectangle.

Now, write a member function to 'move' the rectangle from one position to another position at a given speed.

Now, write a program that asks for length and width and prints the area of the rectangle.

That'll do it!  Enjoy!



1 comment:

  1. I've fixed a bug in my code that Rafael found for me (Thanks Rafael)! In general, you don't want to put member variables outside of your __init__ class... because it becomes a class attribute... (for all members of the class! eeek!)

    So this is wrong:
    class Location(object):
    #MemberVariables
    x=0
    y=0
    z=0
    def __init__(self,,z):
    self.z = z

    And this is right:
    class Location(object):
    #MemberVariables
    #x=0, y=0, z=0
    def __init__(self,,z):
    self.x = 0
    self.y = 0
    self.z = z

    ReplyDelete

Followers