Z
Ô
I
O
N

Erlkönig: Meeting Python

First Pythonic Impressions

I started using Python on 2007-11-23, following the tutorial on the Python website, generating the usual collection of small scripts to test various bits of the language. It quickly became clear that Python has an interesting and pretty clean style of expression, along with notably different performance tradeoffs than some other languages I've used.

Whitespace as Control, and Quirky Control Over Whitespace

hilo.py

#!/usr/bin/python # -*- coding: utf-8 -*- import sys import random def Game(): 'Plays one game of Hilo.' min, max = 0, 100 target = random.randint(min, max) attempts = 0 while True: text = raw_input('Guess #' + str(attempts + 1) + ': ') try: guess = int(text.strip()) except ValueError: print 'Oops - %s is not an integer' % repr(text) else: if min < guess < max: attempts = attempts + 1 if guess < target: print 'Too low' elif guess > target: print 'Too high' else: break # you've won! else: print 'Oops - must be between %d and %d' % (min, max) # no `finally:' clause print '%d is correct on attempt #%d!' % (target, attempts) try: Game() except KeyboardInterrupt: print 'Interrupt received' except EOFError: print 'EOF received' finally: print 'exiting.' #---eof

Once past generating the obligatory Hello World and various code snippets to test parts of the tutorial, I wrote a small version of Hilo to test flow constructs, input from stdin, and exception handling with a project where I'd be fitting the language to the problem rather than the other way around. Some of my initial reactions were:

From 7, to 100 Frames per Second

About the only feature I didn't want to carry over (yet?) was from the C++/Life experiment with a one-thread-per-cell approach, where the cells communicated directly with their neighbors, yielding some 7000 threads in a 140x50 cell automaton and a rather large number of mutexes. Reducing the per-thread stacksize to 1 KiB was a key enabler.

Next, I started writing yet another Life cellular automaton, since I wanted to see how Python felt with respect to a familiar project that I'd written in a C/C++ environment where function calls are relatively cheap, and which would force me to reconceive the design once I was faced with a working, but slow, program. I also wanted to try out classes this time, deriving TTY and curses-focussed subvariants from base classes Cell and Grid, find the terminal size with TIOCGWINSZ even outside of curses, use select to check for input, and do profiling. The profiling was done with 140x50 cell grids, allowed to settle to a collection of trivial spinners and fixed configurations. Some of the surprises at this stage included:

Using SWIG to Integrate Python and a C varags Function
- or Oh, why did I have to pick a variadic function first?
- or Oh, should I have tried ctypes first?

Of course, the real vexer here was starting my SWIG experimentation with a variadic function like printf, except worse, since this one allows for format continuations in later parameters. This ends up interleaving format strings and data, making the determination of the number of args basically impossible without running the full parsing pass outright on the whole chain of format string(s). That didn't stop me from rewriting the variadic function in question to have an alternate form taking its following args in a vector, macrozing around the va_arg calls to allow the same code chunk to live in both functions via an include. Once SWIG started grumbling about the void** second parameter, though, I decided to take a second pass at this will be with something other than a worst-case-scenario,

The latter, of course, went better, reaching some use of vectors fairly quickly, allowing interaction as shown below, based on a gratuitously-recursive C function along the lines of int Sum(int count, Int *vec) with Int just being a struct wrapped around an int (swigc is just the name of the test module, not an official name). Trying to %extend int rather than Int resulted - unsurpringly - in errors, but with Int, the following works just fine:

>>> vec = Vec([11, 12, 13, 14, 15, 16, 17]) >>> swigc.Sum(vec.len(),vec.vec()) 98 >>>

The %typemap approach is looking like the way to go next, since it should allow the Vec class to be jettisoned and the simpler passing of native Python lists into function calls. However, I'm also reading (and hearing) some rumors that a completely different mechanism, ctypes, might be preferable to SWIG in situations without and existing base of SWIG code.

AJAX with Python

While experimenting with an initial AJAX webpage, I realized that by default I was about to promote my sh-based CGI backend to PERL, and that going to Python instead might be more interesting. It only took a couple of minutes to find a convenient Python library (via import cgi) for CGI data parsing as well as a instant debugger option of:

import cgitb; cgitb.enable(display=0, logdir="/tmp")

Between these two, I found myself with Python instead of PERL in under five minutes, and without the need to drag in the PERL CGI parsing functions I've been using for so long now.



contact ζωιον