Ottawa Python Author's
                            Group
 
python.org

Club Information

Membership Instructions
Meeting Information
About the Website

Resources

OPAG Wiki

Documentation

Articles
Contact Us

Current Location: Home /Articles / Article 1

Why I like Python

Why do I like Python? The answers range from simple to complicated. If you've come to this website, you are either already a fan of Python, or you are interested in Python...or you are horribly lost. Hopefully my words here will have some influence over how you look at the language. I've tried to not turn this into a direct competition between Perl and Python, as I use both languages where appropriate, but due to my in depth knowledge of Perl I just can't help it. Don't worry, I trash a few other languages along the way for fun.

Before I get into it though, let me make one thing clear. There is no silver bullet. None. Nada. Zero. No one language is the language. Use the right tool for the job, and don't make everything look like a nail by always touting a hammer.

What's Python?

Python is an Object-Oriented scripting language. What this primarily means is that it's a high-level language in comparison to most compiled languages, usually permitting greater ease in getting your job done without worrying about the sundry details of memory allocation, managing strings as individual characters, and other tasks unworthy of your average coder's time. In this way, Python is no different from other high-level languages like Perl and Tcl. Ending my analysis there would be a horrible crime, so I'll continue whether you want me to or not. So there.

Like Perl and Tcl, Python can be used for simple shell scripts, where one expects to be able to crank out perhaps one-hundred lines of code or less, very informally, to get the task done. Like Perl and Tcl, Python can also be used to build more complex software systems, composed of thousands of lines of code across many modules produced by many developers. It is certainly a large source of argument as to how many lines of code are appropriate for a language like Python, and when you should let the more formal languages like C and C++ take over, but that's a topic for another paper.

However, unlike Perl and Tcl, Python was designed with Object-Orientation in mind from day one. It doesn't require a retrofitted hack to do OO (oops, was that a shot at Perl?), or require an entirely new language (sorry IncrTcl fans...). Oh, and it's still reasonably fast (can't...talk...starting...JVM...). It was designed with clarity in mind, focusing not only on ease of comprehension of the language and its features, but ease of inheritance of the work of others. While this last point may not seem very important from an academic standpoint, out in the real world, people leave and other people have to inherit the work that they've done. The time it takes to understand and absorb that work is called "money."

Python's Syntax

One of the things that makes Python stick out from the rest of the programming languages that I've seen so far is the syntax. It's very clean due to this syntax, which is one of the benefits of the language.

The reason why most languages seem cluttered compared to Python is due to the mechanisms that they use to break up statements in the language. Most languages have special characters or reserved words to delimit blocks and statements. For example, C uses curly braces to delimit blocks and semicolons for lines.

for (i = 0; i < limit; ++i) {
    if (! strcmp(word[i], "hello"))
        puts("found a hello");
}

This means that at the very least, one must add these special characters to permit the language to work as desired, enabling the compiler to determine where statements and blocks end. This is due to the fact that most compilers don't care about whitespace (spaces, tabs, newlines...). That is, most compilers. What if whitespace mattered? Blasphemy!, you say? Think about it. You wouldn't need all those special characters anymore, you could use whitespace to help the compiler determine how to interpret blocks and statements, and the code above would look something like this:

for word in words:
    if word == "hello":
        print "found a hello"

There, isn't that easy? Looks clean, doesn't it? That's the point.

Now, your first reaction might be something along the lines of a very serious convulsion, possibly a seizure. Get over it, and look at this objectively. Most people seem to assume that their personal style is now destroyed, but I'm not entirely sure how that could happen, considering that coding style often involves a vain attempt at being distinctive in the way you place your braces, or whether you put a space in front of your semicolons. Ask yourself this: do you indent your code? If yes, then what's the problem? Indentation in Python is mandatory, sure, but you were going to do it anyway. If not, stay away from any project I'm on, because if you can't handle such simple coding conventions, I worry about what else you can't handle.

"But I like using multiple lines to make my code more readable!", you say? Me too. Don't worry, you still can. You see, as long as a parenthesis of any kind is still open, the Python compiler continues the line. If that fails, you can use the backslash (ie. ) ala Tcl to continue the line. So, this is perfectly acceptable:

language_dictionary = {
    'python':       'Mmmmm. Crunchy',
    'perl':         'write-only language',
    'tcl':          'no POSIX until TclX',
    'java':         'write once, test everywhere',
    'ruby':         'pascal/perl/eiffel/php still ugly',
    'lisp':         'lots of irritating stupid parentheses',
    'C':            'the choice of a GNU generation',
    'C++':          'at least its standardized'
}

Or, if you call functions with lots of parameters, this is fine for readability:

parse(
    lots,
    of,
    important,
    parameters,
    that,
    willnot,
    fit,
    on,
    a,
    line)

Personally I find the cleaner syntax of Python far outweighs any cramping of my style by the language telling me how to format. I suppose if I didn't like indenting, and loved typing extra characters like $ and ; and {, then I'd be really annoyed at Python's lack of such requirements. Luckily I have no such qualms.

Just as a comparison, I recently wrote a prime number generator in Tcl, Perl and Python (not to mention a few other languages), and it gave me the chance to really look at the syntax of the languages. Initially I thought that Tcl was unbelievably slow, as it was over twenty times slower than Perl and Python, but a friend of mine who codes in it all the time helped me optimize, and pointed out the problems with Tcl and double interpolation. The following snippet is from the prime function that calculates whether a given number is prime based on previously found primes. My friend helped me get the Tcl code slightly faster than my Python, but check out the cost in keystrokes.

proc prime {number primes} {
    foreach prime $primes {
        if {[expr {$prime * $prime > $number}]} {
            return 1
        } elseif {[expr {($number % $prime) == 0}]} {
            return 0
        }
    }
    return 1
}

How's that for carpel tunnel? To be fair, Tcl wasn't designed for heavy mathematics, it's more of a glue language, so that routine should truly be in C, glued to the Tcl. I was surprised when it ended up marginally faster than my Python, but I wasn't surprised at all when my Perl was faster. I've been coding in Perl for some time, and I know how optimized it is, and how to optimize my code. Here's a Perl example of the same thing.

sub prime($\@) {
    my ($number, $primesRef) = @_;
    foreach my $prime (@{ $primesRef }) {
        if ($prime * $prime < $number) {
            return TRUE;
        }
        elsif ($number % $prime == 0) {
            return FALSE;
        }
    }
    return TRUE;
}

Easier to read certainly, but still kinda wordy. I guess I could drop the pseudo prototyping, since it's only party functional in Perl in the first place, which is yet another gripe of mine about Perl. I like proper function signatures. I don't understand why they weren't in Perl from day one, except that Larry perhaps wanted to keep a Bourne-shell feel to Perl. I hear they're going into Perl 6 anyway, so that should remove that complaint. If you truly want clean though, it's tough to beat this Python example.

def prime(number, primes):
    for prime in primes:
    if prime * prime < number:
        return TRUE
    elif number % prime == 0:
        return FALSE
    return TRUE

It wasn't nearly as fast, but I admit I'm still too new to Python to know how to optimize it much. If anyone can offer a better version of this, I'll update this page. At the moment the Python and Tcl are about half as fast as the Perl on my P-III 450, using Python 1.5.2 and Perl 5.6. Hopefully it'll catch up, because I'll probably be using Python even if it is a little slower. I didn't bother with a Java example, as I didn't have the time to sit there that long. Compile it you say? Hell, I'll just use C++ then. At least C++ still has operator overloading. I could compile the Python or the Tcl and it'd be far faster and more portable.

Python's syntax is designed for readability, and a consistent look. I love the fact that if my coworkers don't indent properly, the code won't work. Now I don't need a beautifier, and I don't need to beat my co-workers over the head for not bothering to indent consistently.

Strings in Python

I don't know about you, but I really like format strings from C. Once you get the format syntax down, I find it very easy to produce nice output. Python makes heavy use of format strings, primarily because they don't have funky characters in front of the variables to tell the compiler that you want the value interpolated. While those funky characters make for shorter output strings, they clutter up the code in the long run.

All variable substitution into strings in Python is expected to be done via format strings, so if you know them from C, you're all set. Here's a typical example:

print "The value of a is %s and the value of b is %s" % (a, b)

You see, the % operator is the equivalent of a C sprintf() call. It's pretty simple. Now, you could use commas to chain arguments to the print expression like so:

print "The value of a is", a, "and the value of b is", b

but personally I prefer the former.

I think my favorite thing about Python strings is that they are sequences, like in C. Strings are arrays of characters in C, and that's very handy when you want to crawl across the string for some reason. You can do the same in Python, as long as you remember that strings are immutable, meaning that you can't change the string object. You can make new ones though, using Python's very intuitive slice syntax.

import os
for file in os.listdir(os.curdir):
    if file[-5:] == '.html':
        print "found an html file!"

Now, in Perl you can do the same thing of course, but not as easily, as strings are not sequences in Perl.

local *DIR;
opendir(DIR, '.') or die "Can't open directory: $!\n";
foreach my $file (readdir DIR) {
    print "found an html file!\n"
        if substr($file, -5) eq '.html';
}
closedir DIR;

Of course, you have to remember that the eq operator works on strings only, and == is for numbers, and then you have to remember to localize your typeglob to the directory handle or it will be global and you might clobber one that's in use somewhere else in your code. No one would code it this way though, as the standard Perl solution would probably go a little more like this:

local $_;
local *DIR;
opendir(DIR, '.') or die "Can't open directory: $!\n";
my @htmlfiles = grep { /\.html$/ } readdir DIR;
print "Found the following html files: @htmlfiles\n";
closedir DIR;

Regular expressions are something that, once you start using them, you'll wonder how you ever lived without them. Perl did that to me when I learned it, and I'm so happy that the Python developers have ported Perl's regexp library, so I can do the same as above:

import os, re
htmlfiles = filter(
lambda file: re.search("\.html$", file),
os.listdir(os.curdir))

Some expressions in Python are shorter than the Perl equivalent, some longer. Perl is certainly the more cryptic of the two, but that's only to say that the code can be hard to inherit.

As of Python 2.0 and onwards, strings are now more properly objects with methods, just as the list and dictionary in Python are. I find that quite intuitive, and apparently the Perl developers do as well, since they plan to implement the same thing in Perl 6. I'll have to revisit this when Perl 6 is commonplace and see how things compare then. All things change, but today I prefer Python, even though I work in Perl often and like it very much.

Exception Handling

One of my favorite features in Python is the real exception handling. Many languages, C and Perl included, rely on integer return values to indicate success or failure. This is a typical Cism for opening a file:

FILE *filep;
if (! (filep = fopen(path, "r"))) {
    perror("Can't open file");
    exit(errno);
}

It can get quite labourious to do this every time you perform a system call. The Perl equivalent is shorter, but still a pain to do every time. And I won't go into the odd-ball known as Perl filehandles. Why they're different from standard Perl variables, I have no idea beyond bad design.

local *FILE;
open(FILE, "<$path") or die "Can't open file: $!\n";

While the "or die" syntax is cute for the first week or so, in feeling like you're threatening the file to open on point of death, it gets quite tiresome after 10000 lines of code. Trust me on this one. How much fun does this look like?

local *INFILE;
local *OUTFILE;
open(INFILE, "<$infile") or die "Can't open $infile: $!\n";
open(OUTFILE, ">$outfile") or die "Can't open $outfile: $!\n";
$buffer = <INFILE>;
print OUTFILE $buffer;
close INFILE or die "Problem closing $infile: $!\n";
close OUTFILE or die "Problem closing $outfile: $!\n";

Does "or die" still look like fun? How about this?

try:
    infilep = open(infile, "r")
    outfilep = open(outfile, "w")
    buffer = infilep.read()
    outfilep.write(buffer)
    infilep.close()
    outfilep.close()
except IOError, errmessage:
    sys.stderr.write(errmessage + "\n")
    sys.exit(1)

Any cleaner? I thought so. This is all possible because of exception handling. Higher up you can have a try/except around the entire thing, with one set of error handling routines, resulting in n:1 possible errors to error handling code, instead of a 1:1 relationship. That can make the difference between your eyes bleeding and ending up with carpel tunnel or not.

Now, Perl coders will tell you that you can use Perl's eval statement in the same way, and they're right. So, if they're right, why is it that no one does? Look at the sourcecode from modules in the CPAN. It's pretty rare. That's because the basic error handling mechanism is still integers, and retroactively slapping on eval() and $@ won't change that. It's simply not as useful as a system designed with real exception handling in mind from day one.

The above snippet also demonstrates just how clean and intuitive objects can be too. I love Python's OO syntax.

Modules

When I first got into Perl programming, I was impressed by the facilities for modules, with strong namespace support. I started creating library upon library for code reuse, and I was equally impressed with the facilities for supporting distribution of those modules, testing, installation, etc; I still am. I was a little bothered by the overhead in creating a package, but I quickly got used to it. I never got over the "1" hack required of all Perl modules, where you must put a 1 at the end of the file or it might not work. That's an inexcusable kludge. Still, I made many a module in Perl, and I was fairly happy with it.

Then I explored Python. In both Perl and Tcl, you must explicitely declare the namespace that the library is in. In Python, it is automatically the name of the file. For example, if you create a module called mymodule.py, that is the namespace that the module is in. Simple. Additionally, all Python programmers are encouraged to make their modules work as standalone scripts if possible, protected from execution by the standard:

if __name__ == '__main__':
    main()

The wonderful thing about this convention is that if you stick to it, every script you write is a potential module, without any extra effort involved. Once you find yourself in the real world, working at a company that produces software for revenue, you'll find that you often don't have as much time to put the effort into producing libraries the way you should. Anything that helps is welcome.

For a while, something that made me wait until Python was more ready was the lack of a standard module installation procedure, but disutils has fixed that worry. There are still two issues that point to Python's relative immaturity in comparison to Perl. The first is a lack of a repository to rival the CPAN. Python currently gets around this by shipping a large standard library, the so-called, "batteries included" philosophy. The second issue is the "flatness" of the standard library. It is not currently organized very well, and most of the namespaces are at the top level. I'm told that this will change, however.

Unix API Calls Identical

Y'know, there's nothing worse than a language invented by someone who changes things just for the sake of changing them. Some people, myself included, work in multiple languages, because no one language is ever the right tool for every job. If think you've found a silver bullet, feel free to challenge this. I love Python, but I won't write a driver in it, and I don't plan to do CGI programming in C. As such, it's really annoying when you're used to say, deleting a file with the unlink system call in Unix, and then finding a language that calls it delete, or remove, or erase, because the designer thought it was easier to remember. Easier for him maybe, but I'm in the real world. Python calls it unlink, and they also tossed in a remove, so people have choice. It might be called something else on another platform. I wouldn't know.

Python is getting big in the Windows world as well as Unix, but it seems to be keeping its origins while it moves on to new platforms. I like that. I want fctrl to stay fctrl, open to stay open, seek to stay seek, etc. PHP is good for this too, having a very Unix-like API, with web extensions. If this changes, I might end up leaving the language behind, since I tend to work mostly on Unix, and I don't work strictly in one language, so I want the API calls to stay the same to ease my work.

When I come across a language that changes things just for the hell of it, I get annoyed, and I don't want to use it. Eiffel is a great example of this. I'm sorry, but "expect-when" instead of "switch-case"? Why, because common English terms are easier to understand? 70% of the planet doesn't speak English. Most coders already understand "switch-case" though, so you're actually making things harder.

What's Wrong with Python?

Python is not perfect, nor will it ever be. There are some pet peeves that I have about Python, and I have those about all languages. So far, my peeves about Python are:

  1. Performance:

    Python performs well, but it does fall behind Perl performance-wise, and I'd like to see that improve. I hope that is given attention soon.

  2. Windows:

    And by that, I mean Microsoft Windows. I see many windows references in the Python material, in the standard ConfigParser class, and in other places. I think it's great that Python exists on windows as well, and might displace that horrible VB toy of a language, but I am opposed to Microsoft's efforts to force their inferior products down my throat, and I automatically recoil now at windows references. I have strongly considered sticking to Perl, just because it has such strong Unix backgrounds, and it has kept them. I can just ignore any module beginning with Win32.

Random Likings

More to come. <!-- slices are very intuitive to use

error = sys.stderr.write

tie interface unnecessary when you have operator overloading and UserDict/UserList

shortcomings: - needs a CPAN -->

A Django joint.

Quick Links
The Python Homepage
Dive Into Python
Python 2.4 quick reference
Python 2.5 quick reference
ActivePython
Mike Soulier's Homepage
Ian Ward's Homepage

Archived News
May meeting planned
April meeting tuesday
February meeting planned
Django 1.0.2 in production
April meeting firming up
Firming up the March meeting
OPAG Linked-in group created
Porting opag site to Django 1.0
October meeting
Slides from OSBootCamp

Full archive

Admin