Main Content

A demonstration of how many Python facilities work together

Archive - Originally posted on "The Horse's Mouth" - 2011-09-16 06:06:33 - Graham Ellis

Many of our demonstrations on the Well House Consultants site show individual features of a language - taken in isolaion to show you how they work. However, during a course we usually write further examples to show you how features work in combination to give a total result / solution to an application.

On yesterday's Python course, I wrote such a complete example showing many of the features and how they interact - complete source code (opens in a separate window) [here].

What does that illustrate?

Decorators

A method which can be applied as a wrapper around other methods. In my example, I used a decorator to count the number of times each method was called so that I could end the running or my program with a "coverage map" - a table of how many times each method had been called. That's potentially very useful when we come to look at efficiency later!

Factory

A factory method - a piece of code that's within a class or group of classes that takes raw data in what's probably a standard format for a series of applications based on the same data and converts it into an object or objects, with the class (type) of the object being created actually being decided based on the raw data rather than having to be worked out in the user application.

Inheritance

How all the common code to a lot of slighly different types of data can be held in a single class, and the separate classes based on the initial one can be defined just in terms of the changes / differences, rather than each having to be defined completely itself and potentially with a lot of duplication.

Comparators

Object (dynamic) methods usually work on a single object. Class (static) methods usually work on a class as a whole. However, for overridden operators such as + (__add__) you want methods that work on two objects. The sample program we wrote shows how you can pass in two parameters - one before the "." and the other as a conventional parameter in order to write two-object methods.

Polymorphism

The characteristic of different methods being called (and so different logic) being run by a single call in the source code. If you have a variable that may contain an object of type "a" or an object of type "b", you don't have to work out which it is in your own program to call the appropriate logic - Python (or any OO language) does that for you.

To string override

When you print out an object, what do you get? In the generic / default case, all your program can do is tell you the type of object and the address at which it's stored - that's the only common data. However, in Python you can provide a method called __str__ which tells Python how to convert a particular type of object into a string for output purposes.

Formatting

Default output for floats is 14 significant figures. For integers, the interpretter uses a filed just as wide as it needs. Our example shows you how you can override the default format using the % operator.

Exceptions

Exceptions are a way of catching errors when things go wrong, and of handling function calls where they need to return the answer that "there is no answer to that". Exceptions are valuable as a safety net under code which may (often through user error) need to perform some sort of recovery process - perhaps as a result of being asked to read data from a file that does not exist.

Static methods

Most methods in a class will operate on specific object members of that class, but you may ocasioanlly have a method which you want to run on the class as a whole - for example to give you soe interal statistics about the class. You may also want to use static or class methods for utility code which is naturally written and maintained and loaded with the class, but does not refer to specific existing objects. A good example of this is a factory method

Object state caching

If you have complex calculations to do on an object, should you do so as you set the object up, or as you call for the results? As far as the object user is concerned, it doesn't make any difference - if you go to eat in a restaurant, it doesn't matter if the table was set just after the previous eaters left or just before you arrived. But it CAN, in coding terms, make an effiency difference. An efficient way is to calculate a result only when it's needed (for the first time), but then store the result so that it can be called up again from that store if it's requested second and subsequent times.

__class__.__name__

Only VERY rarely should you need to ask "what type of object is this". You can do so using .__class__.__name__. You can also use the isinstance operator to find out whether an object belongs to a class (and that means that it's a member of a class or of a class that's derived from it. That very rare occasion, by the way, is when you're wanting to check that one of your users has called your method with the right type of data.

Test Harness and Unit Testing

The test
  if __name__ == "__main__":
lets us include a test program within a module, which runs if the module is called up directly at the command line. For more thorough testing, you could use the doctest or the unittest module ... and there's an example of the unittest module in use to show how to test the class that I'me writing about in this example [here].

Generator / Iterate through file handle

You can "slurp" a whole file into a list using readlines, but that can result in a huge memory footprint for your process if you have a huge file. The xreadlines method was added a number of releases ago as an iterator to let you read data just ahead of yourself (see also - Generator); that's now been further enhanced so that you can simply iterate through the file handle in a for loop:
  for line in fh:

pass

When Python requires a block but you don't want to do anything in the block, you can use a pass.

*args and **kwargs

Positional command line arguments can be picked up by individual name ... or they can all be read into a list if you put a * in front of the receiving variable name in the function / method definition. You can also apply named parameters to a function or method in Python, and they can all be picked up using the ** notation. It's become a sort of convention to use the names args and kwargs for these. When would you use these? When you're writing a decorator, for example - you want the flexibility of being able to wrap just about any method into another, irrespective of how many or what type of arguments it's called with.

import

Loading modules from elsewhere .... you can use from to bring them into your own namespace, or import to bring them into their own namespace. From make for quick and easy referencing to them, but is not in general recommended since it can quickly lead to shared / duplicated / overwritten names. Especially if you bring in a lot of extra code, you should do so with import

The sys module

The sys module provides you with a whole load of accessors to your immediate environment. In just this one example program, I have used:
• sys.stderr - for output of error messages
• sys.argv - to collect the program name (argv[0]) and command line parameters
• sys.exit - to return to the calling program with a status code
• sys.stdin - to make specific reference to keyboard / redirected or piped input channel

stderr

You can output from your program to the default stdout amd to stderr - both of which will default to your user's terminal window. But you are strongly encouraged to send error messages and log progress to stderr. By doing so, you're providing your user with a way of redirecting only the data output to a file or another process, leving the operational messages still sent to the screen where the user can read them.

Parent Constructor

Objects are created through constructor methods. They can be placed in any class, and be inherited to that you don't have to specify one for each and every class. However, if you want to describe how to define an object in a subclass to be "like the one in my parent class, but with the following differences", how can you do it? By providing a constructor that uses the super call to invoke the parent's constructor, and then make any changes / additions that you need for the subclass.

direct variable access and attributes (properties)

You can reference variables which are held within your object directly. Quick and easy to write the code, but potentially very hard to maintain as you're removing the code layer where you can make changes between the user's calling code and the storage type and name chosen within your objects.

You CAN access a name without () notation if you define it as a property. This:
  people = property(getpeeps,None)
allows me to add .people onto the end of an object name and cause the getpeeps() method to be invoked at runtime. The use of None means that I do NOT have an equivalent way of writing to the pseudovariable under the name .people.

Properties provide the user of your classes with a syntax that's the same as that used for direct variable access, and thus an escape hatch if a lot of user code makes direct variable calls which have become impractical to maintain during code restructures.

/usr/bin/env command line

The first line of a Pythn script on a Unix or Linux system may start with #! to indicate the file under which the file is to be interpressted - so
  #!/usr/bin/python
or
  #!/bin/python

This heas meant that the first line of a script has sometimes needed changing as the script is moved around, even between machines running the same OS but differently configured. The env command, consistently available in /usr/bin, may be used to locate and redirect the script to Python no matter where it is on a particular system!

  #!/usr/bin/env python

Read from file named on command line, or from stdin

And finally ... our example program shows an example of a program that reads data from a file named on the command line, or if there is no such file named it reads from stdin.

If you want to learn more about Python ... please have a look at the resources on our web site - there are many listed subject by subject [here], or come on one of our Python courses.