Originally Published: Wednesday, 30 August 2000 Author: Jason Tackaberry
Published to: develop_articles_tutorials/Development Tutorials Page: 4/6 - [Printable]

Programming with Python - Part 1: Baby Steps

This tutorial is the first in a series that will introduce you to Python, a dynamic, object-oriented language that is rapidly gaining popularity. Since I myself have garnered a modest Perl background before learning Python, I will target this tutorial at the Perl programmer. However, anyone with a little programming experience should find this text useful.

  << Page 4 of 6  >>

A Classy Example

An object-oriented language like Python must provide a way to create classes of objects. In essence, classes are a description about what methods (or member functions) an instance of that class provides, and the semantics of those descriptions.

First things first: we need to clear up some definitions. Python objects are not just instances of classes. Instances are objects, but not all objects are instances. Many of Python's built-in types are objects, but not classes. Earlier I talked about string objects. There is no string class from which these string objects are created. And to make matters even more confusing, classes are objects too. So, for you C++ programmers, if I talk about an instance in Python, I'm talking about what you'd call an object. But when I talk about an object, this is not necessarily an instance, unless of course it's an instance object. Clear as mud, right? This paragraph reads like Abbott & Costello's Who's on First, I realize. Reading it once more couldn't hurt.

The simplest class definition looks like this:

  class coordinate:
    pass

The pass keyword is one we haven't seen before. This keyword is a no-op. Where the Python syntax requires something and you don't actually want to do anything, pass is what you want to use. You may be wondering what good this coordinate class is? There are no methods for this class, but Python does not require variable declarations. So, we can use this class to hold any variable, or attribute, we want. Consider:

  plot = coordinate() # Create a new coordinate instance
  plot.x = 10
  plot.y = 5

If you're thinking this seems to be functionally similar to structs in C, you'd be right. Empty classes provide a place to assign attributes. If we want a 3D coordinate, we can just assign a value to plot.z without any additional work to the class definition. Let's see what a more fleshed out version of the coordinate class might look like:

  class coordinate:
    def __init__(self, x, y, z = 0):
      self.x, self.y, self.z = x, y, z

    def translate(self, xoff, yoff, zoff = 0):
      self.x = self.x + xoff
      self.y = self.y + yoff
      self.z = self.z + zoff

    # rotation about the origin
    def rotate(self, angle):
      from math import * # import the math module into the current scope
      rad = angle * pi / 180 # convert to radians
      self.x = self.x * cos(rad) + self.y * -sin(rad)
      self.y = self.x * sin(rad) + self.y * cos(rad)

This example introduces some new syntax to us. First, we see right away that the coordinate class defines three methods: __init__, translate, and rotate. The __init__ method, as you may have guessed, is special. This is the constructor for the class -- the method that is called when a new instance of a class is created. The z = 0 that appears in __init__'s parameter list will be familiar to C++ programmers. It denotes that this argument is optional, and if it is not specified, it will default to 0. Also, the first line in the body of the constructor is a tuple assignment. It would more explicitly be written as (self.x, self.y, self.z) = (x, y, z), which will friendlier to Perl programmers. This does an element-by-element assignment; so self.x becomes x, self.y becomes y, and self.z becomes z.

Draw your attention to the first parameter of each of the 3 methods. The first parameter of any method is an instance object -- the instance from which this method was invoked. The parameter name self is arbitrary. You could call it this if you prefer. However, the name self is a Python convention, and I strongly recommend against using anything else.

Scoping rules in Python will be discussed in detail in part 2 of this tutorial. For now, it will have to suffice to say that in order to reference an attribute of an instance, you must reference it through the instance object, or self. So while in a C++ member function this->attr and attr are the same (assuming attr has not been redeclared in the member function's scope), in Python self.attr and attr are not. Now your first instinct might be to say this is a silly way of doing it. Why not just make a this keyword, like in C++? By passing the instance as a parameter, we more clearly and explicitly couple a method to an instance. Methods are also objects, and can be either bound to instances, or not bound to anything (that is, unbound). Let's see what I mean:

  plot = coordinate(2, 2)
  print coordinate.rotate
  print plot.rotate

Whose output is:

  <unbound method coordinate.rotate>

  <method coordinate.rotate of coordinate instance at 80cabb0>

Here we demonstrate a neat feature of Python objects: they can represent themselves as strings. This is quite useful for debugging purposes. The built-in function repr returns a string containing such information; the print statement will implicitly call repr on most objects for you.

So the first line of output tells us we have an unbound method. That is, it isn't associated with any instance of the coordinate class. The second line tells us it's a method of an instance, and so it is bound. The numbers at the end of the string represent the address at which this instance is located. Obviously it's going to be different for you.

In order to make my previous statement that "classes are objects" more obvious, let's look at this snippet:

  print repr(coordinate)
  fooclass = coordinate
  print repr(fooclass)
  plot = fooclass(2, 2)
  print plot

Which will output:

  <class __main__.coordinate at 80cbda8>
  <class __main__.coordinate at 80cbda8>
  <__main__.coordinate instance at 80bf228>

The first two print statements output the same thing. And this really does make sense, because the variable fooclass is bound to the class object coordinate. When we call fooclass(), we're really invoking coordinate's constructor, and the result is an instance of coordinate.

The last piece of the puzzle is the occurrence __main__ in the output. We'll be getting into scopes and namespaces in part 2 and we'll address this in more detail then. For now, it's easiest to think of __main__ as the top-most, or global scope. It's where all objects that aren't part of another module or class end up.





  << Page 4 of 6  >>