CHAPTER 10

image

Classes

Classes are the way that we create objects. We use objects to try to model the real world in computer code. Objects are a way of encapsulating programming code that, not only can be reusable, but can be duplicated and modified without affecting the original object. Objects often have attributes and functions to modify those attributes. Classes also allow you to write a group of code that may be used in multiple projects without rewriting or copying the code into each project just like a library. We will concentrate on objects in this chapter, all though the concepts are the same for simple classes.

What is an object?

When trying to explain what objects are, I like to use the example of a car. What is a car? It’s a thing that has a body, a frame, an engine, a number of wheels, and more. The body type, the frame type, the engine type, the number of wheels, the color of the body, and other things are all examples of the attributes of the car. All cars have, for example, doors. However, the number of doors can change from model to model of cars. Some have two doors, some four, and some five (if you consider a trunk lid a door). We can create a “generic” car (called an instance) and then for whatever type of car we want, we can modify the attributes of that instance of a car to suit our own purposes. This is called inheritance. The new “model” of car we create by modifying the attributes, inherit the attributes of the parent.

Creating a Class

When we create a class, we use a class definition, very similar to a function definition. The class definition starts at the first position on the line with the word ‘class’ and ends with a colon. Any code lines within the class are indented. The first unindented line in code is considered outside of (not part of) the class.

class ClassName:
    {Any accessable class variables}
    {initialization routine}
    {Any functions you may need}
    ...

Here is an example:

class Car:
    __init__(self,attrib1,attrib2):
        pass

    def Function1(self):
        pass

     def Function2(self):
         pass

This is all fairly self-explanitory, with the possible exception of the word “self” in each of the definitions of the functions. The word “self” refers to the fact that we are using a variable or function within the specific instance of the class or object.

An Actual Example

Let’s look at an example of a class and an object created by that class. We will create a class called “Dog”. The class will have the following attributes:

  • name (dogname)
  • color (dogcolor)
  • height (dogheight)
  • build (dogbuild)
  • mood (dogmood)
  • age (dogage)
class Dog(): # The class definition
    def __init__(self,dogname,dogcolor,dogheight,dogbuild,dogmood,dogage):
        #here we setup the attributes of our dog
        self.name = dogname
        self.color = dogcolor
        self.height = dogheight
        self.build = dogbuild
        self.mood = dogmood
        self.age = dogage
        self.Hungry = False
        self.Tired = False
  

Most classes (and object) have an initialization function. This is automatically run when we create an instance of the class. To define the initialization routine, we name it __init__ (that’s two underscore characters, followed by the word “init” followed by two more underscores) and then the parameter list, if any, and finally a colon just like any function we would normally write.

Within the initialization routine, we set up and define any internal variables or in this case attributes. Notice that we are also defining two attribute variables called Hungry and Tired that are not part of the parameter list. Normally, we would not “expose” or reveal these to the outside world and would only allow the internal functions to change them. In order to have variables or attributes hidden from the users of our objects, we start the variable name with two underscore characters. An example would be __Hungry and __Tired. However, because we are keeping things simple, we will expose everthing.

The functions within our class

Within the class we write functions just like any other function. We start the parameter list with the self keyword. If you don’t use the self keyword, you will get some weird errors. When we refer to any of the internal variables, we use the .self keyword. The following three funtions are used to “tell the dog object what to do”. Of course there could be many more, but for this example, we’ll stick with these.

    def Eat(self):
        if self.Hungry:
            print 'Yum Yum...Num Num'
            self.Hungry = False
        else:
            print 'Sniff Sniff...Not Hungry'
  
    def Sleep(self):
        print 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ'
        self.Tired = False
  
    def Bark(self):
        if self.mood == 'Grumpy':
            print 'GRRRRR...Woof Woof'
        elif self.mood == 'Laid Back':
            print 'Yawn...ok...Woof'
        elif self.mood == 'Crazy':
            print 'Bark Bark Bark Bark Bark Bark Bark'
        else:
            print 'Woof Woof'

You can see that the code for these three functions is extremely simple.

The Eat function simply checks the Hungry attribute. If Hungry is True, then the dog object “eats” and then sets the attribute to False. If not, it just prints its line.

The Sleep function simply prints snores and then sets the ‘Tired’ attribute to False.

The Bark function walks through an if/elif/else statement and, based on the mood attribute, prints something appropriate.

Using the dog object

The following line of code will create an instance of the class/object (also called instantiation) and passes the correct information to the class initialization function, if there is one. Notice that this is part of the “main” code, not part of the class.

Beagle = Dog('Archie','Brown','Short','Chubby','Grumpy',12)

We now have a dog object called “Beagle”. As you can see, he’s (it’s probably a he because the name is Archie) brown, short, chubby, and grumpy. Now we use the object. To access any of his functions or attributes, we use the object name (Beagle) followed by a dot (.) and then the function name or attribute name.

In the next five lines of code, we are accessing his attributes.

print 'My name is %s' % Beagle.name
print 'My color is %s' % Beagle.color
print 'My mood is %s' % Beagle.mood
print 'I am hungry = %s' % Beagle.Hungry

The last four lines of code will have him do things or, in the case of the second line, make him hungry.

Beagle.Eat()
Beagle.Hungry = True
Beagle.Eat()
Beagle.Bark()

When we run this program, we get the following output:

My name is Archie
My color is Brown
My mood is Grumpy
I am hungry = False
Sniff Sniff...Not Hungry
Yum Yum...Num Num
GRRRRR...Woof Woof

Going Further

We can extend this example by creating multiple instances of the Dog class. After the line that we initialize the Beagle object, replace all the following lines of code with these lines:

Lab = Dog('Nina','Black','Medium','Chubby','Laid Back',8)
Shepherd = Dog('Bear','Black','Big','Skinny','Crazy',14)
Lab.Hungry = True
print 'My name is %s' % Beagle.name
print 'My color is %s' % Beagle.color
print 'My mood is %s' % Beagle.mood
print 'I am hungry = %s' % Beagle.Hungry
Beagle.Eat()
Beagle.Hungry = True
Beagle.Eat()
Beagle.Bark()
print 'My name is %s' % Lab.name
print 'My mood is %s' % Lab.mood
if Lab.Hungry == True:
    print 'I am starving!'
    Lab.Eat()
    Lab.Sleep()
    Lab.Bark()
else:
    print 'No...not hungry.'

This creates two more Dog objects. One is called Lab and the other Shepherd, which along with the Beagle object, makes three. Running the code results in the following output:

My name is Archie
My color is Brown
My mood is Grumpy
I am hungry = False
Sniff Sniff...Not Hungry
Yum Yum...Num Num
GRRRRR...Woof Woof
My name is Nina
My mood is Laid Back
I am starving!
Yum Yum...Num Num
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
Yawn...ok...Woof

Obviously, there is much more you could do with this example. However, we will move on.

Something Real

Now we’ll concentrate on something that is useful. We’ll create a class that queries the WeatherUnderground website and get the current weather conditions for any given location in the United States by using the Zip Code. Before we go any further, let’s layout a series of requirements for the class:

  • Gets the current weather conditions
  • Gets the Current Temp
  • Gets the Current Barometric Pressure
  • Gets the Relative Humidity
  • Gets the Current Wind direction and speed.
  • Runs in a Command Window (Windows) or Terminal (Linux)

Now we should look at the process that the program will perform:

  1. Get the Zip code.
  2. Instantiate the class, pass the Zip code to the class.
  3. Open a socket to the URL of the website and get the information in the form of an XML file.
  4. Parse the returned XML file pulling the information we need.
  5. Print the information from the parse process.

Before we can code, we need to know what information the website will give us.

Reviewing the Information

The XML file is too large to print here, so I will just give a partial dump of the XML data. The elipses denote that there is more data that has been cut for the sake of brevity.

<current_observation>
    <credit>Weather Underground NOAA Weather Station</credit>
    <credit_URL>http://wunderground.com/</credit_URL>
    ...
    <display_location>
        <full>Aurora, CO</full>
        <city>Aurora</city>
        <state>CO</state>
        <state_name>Colorado</state_name>
        <country>US</country>
    ...
    </display_location>
    <observation_location>
        <full>Aurora, Colorado</full>
        <city>Aurora</city>
        <state>Colorado</state>
        <country>US</country>
        ...
    </observation_location>
    <station_id>KBKF</station_id>
    <observation_time>Last Updated on November 14, 12:55 PM MST
</observation_time>

    ...
    <weather>Partly Cloudy</weather>
    <temperature_string>54 F (12 C)</temperature_string>
    <temp_f>54</temp_f>
    <temp_c>12</temp_c>
    <relative_humidity>41%</relative_humidity>
    <wind_string>From the SSW at 5 MPH </wind_string>
    <wind_dir>SSW</wind_dir>
    <wind_degrees>200</wind_degrees>
    ...
</current_observation>

As you can see, there is a huge amount of information given to us and much of it we won’t use. The items of interest are:

  1. <full> for the location text.
  2. <observation_time> for the time of the readings.
  3. <weather> for the current conditions (partly cloudy, clear, etc.).
  4. <temperature_string> for the temperature.
  5. <relative_humidity> for the humidity.
  6. <wind_string> for the wind direction and speed.
  7. <pressure_string> for the barometric pressure.

If you are not familiar with XML data, I’ll give you a VERY quick tutorial.

XML Refresher

XML is one of a number of markup languages that are based on HTML. XML works on the concept of tags, text and attributes. A tag works like the key in the dictionary key/value pairs (see Chapter 6) and the text is the value of the pair. If there is data for a tag, there will be an opening tag and a closing tag. The closing tag is exactly the same as the opening tag except it will start with a slash. It will look like this:

<tag>text</tag>

Where the </tag> is the end tag. An attribute is something that is part of the tag and contains extra information. In what we are dealing with in this code, we don’t have to worry about attributes in an XML file. In the above example data, when we look for the <weather> information, we are looking for the tag <weather> and the text is ‘Partly Cloudy’.

Time to Code . . .

Now that we know what we are looking for, we can start our coding.

Import the Libraries

We will start by importing the libraries that we need:

from xml.etree import ElementTree as ET
import urllib
import sys

All three libraries are standard libraries that come with the normal Python distribution.

Create the Class

Next we need to create our class. It will be named 'CurrentInfo' and will contain three functions. In order, they are:

  • getCurrents—Does the web access and parsing of the XML data
  • output—does the printing to the terminal window.
  • DoIt—calls the above functions in the proper order.

Although I stated earlier that most classes have an initialization function, this class won’t have one, because in this case there is really nothing to initialize. We’ll start with our class definition and the first function:

class CurrentInfo:
    def getCurrents(self,debuglevel,Location):

There are three parameters for the getCurrents function. The self parameter won’t take an argument or data. The debuglevel is either a 0 or 1 and is there to be a flag to print debugging information or not. Location is where the Zip code is passed into the function. Next, we check to see if the debug mode is turned on and if so, we print the location Zip code that was passed into the function:

        if debuglevel > 0:
            print "Location = %s" % Location

Connect to the Website

Next we attempt to connect to the WeatherUnderground website to get the XML data. We do this using the try/except error handling set. This is in case we have no Internet connection or the website doesn’t respond in a timely manner. After the try keyword, we set the variable called CurrentConditions to the website URL and include the Zip code at the end:

        try:
            CurrentConditions = _ 'http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query=%s' % Location

Now we set the default timeout for the connection to 8 seconds, and open a socket to the URL we set up using the CurrentConditions variable. If there is no error, we tell the XML parsing routine to grab the XML data from the socket, and then we close the socket:

            urllib.socket.setdefaulttimeout(8)
            usock = urllib.urlopen(CurrentConditions)
            tree = ET.parse(usock)
            usock.close()

If there was an error, then we print an error message to the terminal window, and if the debug mode is set to True, we print the location and use the exit routine from the sys library to terminate the program. The number 1 in the sys.exit(1) line says that there was an error that caused us to terminate early. Usually the code 0 means everything worked well, code 1 means “something weird happened,” and 2 means a command line error occurred.

        except:
            print 'ERROR - Current Conditions - Could not get information from server...'
            if debuglevel > 0:
                print Location
                sys.exit(1)

Now, we are assuming that everything has worked correctly and the parser has gotten the XML data. I’m going to explain the first two parsing commands because all of them are the same except what we are looking for and the variables we assign the data to.

The getCurrents function

The first line (for loc in tree.findall(".//full": ) tells the XML parser to look for the tag named <full>. Every tag with that name is pulled into a list named 'loc'. For each and every item in the list, we assign that item text to the self.location variable. In the case of this data, there are many <full> tags, but they all hold the same data. In the next one, we want to get the observation time. Just in case there is more than one instance, we use a for loop to assign the text to the self.obtime variable. Here is the code for this section:

        # Get Display Location
        for loc in tree.findall(".//full"):
            self.location = loc.text
        # Get Observation time
        for tim in tree.findall(".//observation_time"):
            self.obtime = tim.text
        # Get Current conditions
        for weather in tree.findall(".//weather"):
            self.we = weather.text
        # Get Temp
        for TempF in tree.findall(".//temperature_string"):
            self.tmpB = TempF.text
        #Get Humidity
        for hum in tree.findall(".//relative_humidity"):
            self.relhum = hum.text
        # Get Wind info
        for windstring in tree.findall(".//wind_string"):
            self.winds = windstring.text
        # Get Barometric Pressure
        for pressure in tree.findall(".//pressure_string"):
            self.baroB = pressure.text

That’s the end of the getCurrents function. Now we will work on the output function.

The Output Function

This simply prints the information that we have pulled from the XML file into a “human friendly” format in the terminal window:

    def output(self):
        print 'Weather Information From Wunderground.com'
        print 'Weather info for %s ' % self.location
        print self.obtime
        print 'Current Weather - %s' % self.we
        print 'Current Temp - %s' % self.tmpB
        print 'Barometric Pressure - %s' % self.baroB
        print 'Relative Humidity - %s' % self.relhum
        print 'Winds %s' % self.winds

The DoIt function accepts one parameter, the location Zip code. It then calls the getCurrents function with (in the code below) the debug mode turned off and the location. Then it calls the output function:

    def DoIt(self,Location):
        self.getCurrents(0,Location)
        self.output()

That ends our class. It was very simple, but gives you a good idea how easily a class can be created.

The Main Function

The next bit of code is the main function. Again, it doesn’t do very much for this particular program, but it gives a starting point for our program. The first line will assign the Zip code to the location variable. The second line will create an instance of our class. If it had an __init__ function, that would be called automatically when the class instance is created. Finally we call the DoIt function to start the whole thing off:

def main():
    location = '80013'
    currents = CurrentInfo()
    currents.DoIt(location)

Add the Program Entry Point

The last thing we will do is add the program entry point. We use the two lines starting with if __name__ and ending with main(). There are two built-in variables that Python handles for us. The first is __name__ (that’s two underscore characters, the word ‘name’, and two more underscore characters). The other is just like the one we just described but is __main__ (again two underscores, the word ‘main’, and two more underscores). When you start a Python program from the command line, the __name__ variable is set by Python to __main__, so the interpreter knows that it is supposed to call the main() function. However, because we created a class that does all the work, we can actually treat this as a library and import the CurrentInfo class into another program. In that case, the main() function will not be run, because __name__ will not be __main__ but the name of the program calling it.

#===========================================================
# Main loop
#===========================================================
if __name__ == "__main__":
    main()

The output from our program class is as follows:

Weather Information From Wunderground.com
Weather info for Aurora, Colorado
Last Updated on November 14, 12:55 PM MST
Current Weather - Partly Cloudy
Current Temp - 54 F (12 C)
Barometric Pressure - 29.86 in (1011 mb)
Relative Humidity - 41%
Winds From the SSW at 5 MPH

As an aside, I have tested the source code by changing the location variable data from ‘80013’ to ‘W11 2BQ’. Everything worked as expected.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset