| Author: | Bill Sconce |
|---|---|
| Contact: | sconce@in-spec-inc.com |
| Organization: | In Spec, Inc. |
Abstract
A selective introduction to Python, with primary goals of:
This will not be a complete introduction to Python. Most of the wonders of Python, such as its OO capabilities, or the richness of its standard library, will have to be another session. Or an exercise for the student!
EEs in the audience may appreciate a metaphor of impedance matching.
A programmer's brain works in ways (mysterious to us!) which are sometimes powerful, sometimes remarkably narrow and obtuse. Our ability to grasp generalities and visual analogues is astounding. Our ability to resist distractions or see through syntactic cruft is pitiful.
Consider an imagined interface between this remarkable instrument, our brain, and a complex task such as creating a computer program. Information must get in; information and control must get out.
In electronic terms an interface is characterized by the impedance on each side. Efficiency of transfer is maximized when the impedances match.
Python's homely mantra of "it fits your brain" describes this interface, and suggests the goals sought by Python's designers of matching Python's capabilities to the kinds of things which our human brains do well. The impedance match, although not yet perfect, is better than that of almost any other language.
Each time you "enlarge your envelope", pushing through a boundary of knowledge to learn something new about Python, you will be pleasantly surprised. You will often say, "Yes! That's the way I'd have built it."
My challenge is to demonstrate that "Yes!".
I want to give you a head start on your own experience of learning Python. That means building a program or two, and our emphasis tonight will be hands-on.
"Head start" means a selection of tips and illuminations which you're likely to find valuable, which you would find out from your own investigation, but of which you might say, "I wish I'd known about that sooner".
Certain things I'll assume to be obvious, such as what a for loop does.
Certain other things, such as Python's excellent object-oriented facilities, will wait for another evening.
Please do ask questions as we go along. I'd rather give a good introduction to a few things.
Python
Python is designed and developed for the programmer's productivity, convenience, accomplishment, and enjoyment. Contrast with almost every other programming language. If Python is your first language you are especially lucky: Python offers the best learning environment out there. Getting started is easy, every step is rewarding.
After Python you will probably find that no other language will taste fully cooked.
To ease the programmer's burdens, and to make programming fun.
When a language decision must be made which trades off almost anything else for a programmer's comfort and productivity, Python decides in favor of the programmer.
Obviously, building a language requires a multitude of decisions which will affect programmers' lives.
Sometimes tradeoffs seem necessary in favor of performance. Although Python is in fact no slouch in performance (performance does matter!), you as a programmer will almost never be required to put up with wart in the language so that the computer will run faster.
Sometimes tradeoffs seem necessary for backward compatibility. Backward compatibility does matter, especially so to Pythonistas. But when experience in the field has demonstrated a mistake in the language's design which hurts programmers, Python has been changed to fix it.
With Python you will be free of cruft which was created to help compilers read paper tape 30 years ago.
Clarity is one of Python's core design values. You will find that the most natural way of writing Python code happens to yield code which you can easily read next week or next year. Your convenience in picking up and understanding existing code is paramount, and matters greatly in real-world programming.
Language clarity also encourages concept clarity, and this speeds up the programming process.
Everyone who uses Python seems to be having a good time. Python is clean. Using Python you will understand problems more clearly. You will often work much more rapidly, and you will produce better code. You will find great satisfaction in working with Python, and not infrequently amazement as well.
1989 - Guido, then working on the Amoeba distributed operating system at Centrum voor Wiskunde en Informatica in the Netherlands, chooses "Python" (in honor of Monty Python's Flying Circus) as the name for a "hobby project" which would become a replacement for the language ABC.
1991 - First public release.
1994 - comp.lang.python usenet group founded.
Much (or most) of Python and its features have come from good ideas in other languages, among them indentation, namespaces, I/O, virtual machines, lists, dictionaries.
For an authentic tale of genesis, see Guido's foreword to Mark Lutz's book "Programming Python". [GvR96]
From the beginning Python has been a Free language.
Python is one of the few languages in wide use which do not owe allegiance to proprietary interests. It is Free Software - no marketroid distorts its features, no corporation tries to set standards, and there are no limitations on platform.
Changes to Python are well vetted.
(And, of course, flamed sometimes. See the discussions on the proposed ternary operator, for example!)
There's insufficient time to explore even a sampling of Python's features, but no introduction to Python should completely overlook the ones below. Some of these features will be obvious almost immediately as you learn Python, while others may dawn on you only weeks or months into your Python experience.
Where does Python run?
Who uses Python?
Some of these applications give the lie to objections about "performance" of an app which isn't written in a low-level language. In fact, real evaluation of performance rests on objective profiling, and the "glue" logic executed by the Python VM may be an infinitesimal fraction of the overall application's machine load. (Much of the work may well be done in a low-level, compiled language - NumPy is an excellent example, as are most GUI frameworks.)
Python's facilities for rapid development depend on features which make it extremely easy to prototype, to make a tentative test run, to "try it and throw it away".
The first is the lowly print statement, which has been around at least since BASIC.
It gets no respect, but if you aren't using the print statement to test things and to debug you're wasting at least some of your time. In spite of its humble origins it remains immensely useful, and the lack of such a facility in other languages slows you down.
>>> print 2+2 >>> 2+2
In the interactive window you can usually even omit the print keyword! (You can't omit it in a program.)
A possible "wish I'd known about that earlier" note: the print statement allows file redirection:
print >>somefile, 2+2
where somefile is the name of an open output file.
A Python interactive window is right up there with the print statement for lightweight usefulness.
If you want to test something, or you "almost" know the syntax of something you want to do, a few statements in an interactive window let you check things out.
>>> def sum(a, b): ... return a, b, a+b ... >>> sum(3, 5) (3, 5, 8) >>>
For real programming, of course, you need an editor.
For programming efficiency, your editor should be Python-aware. One common feature is syntax coloring, which is nice. What is essential, however, is a facility for instantly running a piece of code while it's being developed.
There are many such editors out there, and there are full-blown IDEs too. More on this later.
Although we're not doing object-oriented design tonight, a quick picture of Python's objects will be a big help in visualizing what's going on even when you're doing common tasks.
Mostly you don't have to visualize what's going on when you're doing common tasks, but when you're doing advanced tasks it's much easier in Python because the same machinery underlies everything.
Executing the statement:
>>> n = 2 + 2
does four things.
An object can be bound to more than one label at a time:
>>> m = n >>> m 4 >>> n 4
results in our integer object having two names.
Your program can now refer to our integer object by either n or m. There are two names, but only one object. (So of course the value associated with each name is the same, 4.)
Just for fun, here's a first example of Python's introspection:
>>> m == n True>>> m is n True
The first test of these two tests compares the value of the object bound to m (4) to the value of the object bound to n (4).
The second test is introspection. It checks to see whether the names m and n are bound to the same object.
To finish with the fun (don't worry about this, we won't need it tonight):
>>> s = 'spam' >>> t = s >>> s == t True >>> s is t True >>> t = 'sp' + 'am' >>> t 'spam' >>> s == t True >>> s is t False
(By the way, using True and False to report Boolean values is one of the very small, and very nifty, features of 2.3.)
In Python everything is an object. Try:
>>> n 4 >>> print dir(a)
The result shows that even so "simple" an object as an integer has a number of characteristics, most notably methods (see the add & subtract?). The double underscores signify that these characteristics are internal - they're Python's worry, although they're not protected:
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__','__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__str__', '__sub__', '__truediv__', '__xor__']
However, if you don't need to use OO features you don't have to. Not any. Python is designed so that you can go as deeply into its object structure as you wish, yet if your needs are simple you can safely ignore the powerful machinery running behind the curtains.
(This also gives you a platform for learning about OO techniques, if you wish to use Python to do that. No other language makes exploration of OO so easy.)
We'll talk more about this if we have time.
Our demo program is going to need to read a file. Here's a file object:
>>> i = open('spam.txt', 'w')
<open file 'spam.txt', mode 'w' at 0x008DD598>
>>> dir(i)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'close', 'closed', 'fileno', 'flush', 'isatty', 'mode', 'name','newlines', 'next', 'read', 'readinto', 'readline', 'readlines', 'seek', 'softspace', 'tell', 'truncate', 'write', 'writelines', 'xreadlines']
Note the methods read and write (expected):
>>> i.read <built-in method read of file object at 0x008DD598>
(Note that Python doesn't tell you i, the name bound to the file object, but only file object at <some_address>. That's because any Python may be bound to more than one name. Or to no name at all.)
For an example of an attribute look at closed:
>>> i.closed False >>>
Finally, note the next method. It's one of the handiest things about file objects - it lets Python make a file available to you in a for loop.
Back to development. Let's get ready to program something. I recommend that you consider keeping three environments available at almost all times during your Python development.
Your favorite printed book will also be handy.
As tonight's primary example let's solve a text-processing problem.
Let's say that you're a pilot, and that you use the Web to get weather information. One important class of weather data is winds aloft, which the National Weather Service forecasts for various altitudes at various locations. This information is available on a NWS Web site.
However, the Web site for the winds suffers from bloat. It has color logos, a panel of links down the left side, a search-engine pulldown window. Let's say that you have to live with a 28.8K modem. Slow.
Finally, only a portion of the real data themselves are usually of interest. Let's say that you usually only want to look at Boston, Albany, New York City, Pittsburgh, and Elkins. Also, let's say that your airplane can't reach altitudes above 18,000 feet. Sounds like an application for a filter.
The part of the NWS page which contains the real data is about two-thirds the way down in 429 lines of javascript and HTML. Here's an extract starting at about line 294:
DATA BASED ON 201200Z VALID 210000Z FOR USE 2100-0600Z. TEMPS NEG ABV 24000 FT 3000 6000 9000 12000 18000 24000 30000 34000 39000 BDL 0917 0817+09 1312+04 1612+00 1827-12 2032-23 225139 226548 235655 BGR 0210 0309+10 0306+07 3306+01 2610-11 2416-24 254340 255749 254655 CAR 3510 0510+11 0213+07 0111+02 0308-12 0512-25 041641 041549 282355 PWM 0312 0113+09 9900+07 9900+01 2111-11 2415-23 234539 245949 245356 EMI 0215 9900+08 2207+02 2118-03 2032-16 2139-27 224839 226044 235149 ACK 0720 0815+11 1207+07 1908+02 2115-09 2123-21 234337 235947 246556 BOS 0620 0514+10 1006+05 1709+01 1916-11 2125-23 224839 236248 236056 BML 0206 0406+07 9900+07 9900+01 2009-12 2212-24 243741 244849 243755 ACY 1111 1508+10 2012+05 2121+00 2137-12 2149-23 237538 238346 236953 ALB 0618 0920+09 1016+04 1213-01 1717-13 1727-24 202840 225350 234253 BUF 0515 0612+06 0815+02 0722-03 1021-15 1025-27 092844 082649 091149 JFK 0925 1018+09 1511+04 1816-01 1938-11 2039-23 226139 227447 236053 [...]
Here's one solution to this filtering problem, using Python. It's the program I'll develop for you.
Please make notes and ask questions:
progname = 'winds' # wjs 03.6.20
I = 'winds_aloft_pagesource.html'
i = open(I, 'r')
for iLine in i:
iTokens = iLine.split()
if len(iTokens):
if iTokens[0] == 'DATA':
print iLine,
if iTokens[0] == 'VALID':
print iLine
elif iTokens[0] in ('FT', 'BOS', 'ALB', 'JFK', 'AGC', 'EKN'):
print iLine[:41]
i.close()
And here's SciTE's log window from running winds.py:
$ $ python winds.py DATA BASED ON 201200Z VALID 210000Z FOR USE 2100-0600Z. TEMPS NEG ABV 24000 FT 3000 6000 9000 12000 18000 BOS 0620 0514+10 1006+05 1709+01 1916-11 ALB 0618 0920+09 1016+04 1213-01 1717-13 JFK 0925 1018+09 1511+04 1816-01 1938-11 AGC 0218 0218+05 0312+00 9900-06 0311-19 EKN 3613+05 3409+00 2906-05 2906-19 $
It wouldn't be fair to fail to give at least the first hint of credit to Python's amazing standard library.
Once we have our little filter program running (developed using a page source text file captured by a browser, perhaps), our pilot might wonder: how hard would it be to have Python do the HTTP retrieval as well as the filtering?
The answer: easy.
The changed lines, not counting the program name, are the three lines marked with #URL. That is, all we do is change the form of the "file" we use as the input. Essentially, Python lets you treat a URL as a file object. All of the low-level stuff, HTTP protocol, socket handling, and so on are handled for you. This is typical of the Python library.
Note that the urllib module also takes care of proxies:
progname = 'winds_url' # wjs 03.6.20
from urllib import urlopen #URL
I = 'http://aviationweather.gov/fdwinds/text/boston_fd2.shtml' #URL
i = urlopen(I, proxies = {'http': 'http://someproxyserver.in-spec-inc.com:8000'}) #URL
for iLine in i:
iTokens = iLine.split()
if len(iTokens):
if iTokens[0] == 'DATA':
print iLine,
if iTokens[0] == 'VALID':
print iLine
elif iTokens[0] in ('FT', 'BOS', 'ALB', 'JFK', 'AGC', 'EKN'):
print iLine[:41]
i.close()
OK, OK, so we know someone will want to discuss how scary it is to go "cold turkey" about braces.
First, a side note: it's not accurate to say that in Python blocking == indentation. In Python blocking is signalled by a colon.
What follows a colon may be on the same line, for example:
if 2+2 == 4: print 'spam'; print 'eggs'; pi = 3.14
Indentation is one of the structure tools (an indispensable one to be sure), but its purpose is to support the colon notation. I make this perhaps pedantic distinction because the power of the colon's visual cue is easily overlooked, and because clarity of visual clues is fundamental to Python.
The nested structure in:
if file_is_empty: break
is instantly apparent because the ":" smacks you between the eyes.
(Besides: in Python braces are too valuable to waste. They're needed for something worthwhile - designation of the dictionary data type. Beginners may not know it, but they really want dictionaries.)
Tongue in cheek, then, here's a possible two step program to begin a recovery from braces.
Starting with an arbitrary snippet of original 1970s-language code:
if (x)
{
if (y)
{
f1()
}
f2()
}
apply a simple brute force translation to yield legal Python syntax:
if (x):
#{
if (y):
#{
f1()
#}
f2()
#}
We can quit at this point. Python is happy, We're happy. Maybe.
Reduction 1 - in Python the "if" statement knows that a Boolean expression is sure to follow the keyword. So the parentheses around conditionals are just extra typing:
if x:
#{
if y:
#{
f1()
#}
f2()
#}
Reduction 2 - languages of 1970 had to handle paper tape, and compilers needed help from human beings about where blocks begin and end. If your eyes are happier seeing braces you can code them, as above. Python is happy. We're happy. Maybe.
But Python, since it was designed after the invention of screen editors, knows where your blocks are. (You do indent.) (Don't you?) So why not save some further typing:
if x:
if y:
f1()
f2()
Now everybody's happy. (Aren't we?)
Python uses collections of objects to make your life easier. An important example is shortcuts having to do with tuples. This is legal (and handy):
a, b, c = 1, 2, 3
Tuples are being processed behind the scenes. An especially handy case:
x, y = y, x # An exchange, in a single statement
Another (more important!) case is an idiom in functions, which in Python can return anything object, including a tuple - i.e., multiple values:
def lookup(employee_id): ... ... return firstname, lastname, phone_number
and the corresponding function call can be like:
fname, lname, pnumber = lookup('123456')
This feature saves all kinds of grief with temptations to misuse globals, passing pointers to structures, and similar dangerous stuff.
Regular expressions are one of the things that many newcomers to Python ask about.
Python has regular expressions, and they are extremely powerful. The literature says that they are equivalent to those in Perl.
Two comments:
For example (note the complilation flag re.VERBOSE):
charref = re.compile(r""" &[#] # Start of a numeric entity reference ( [0-9]+[^0-9] # Decimal form | 0[0-7]+[^0-7] # Octal form | x[0-9a-fA-F]+[^0-9a-fA-F] # Hexadecimal form ) """, re.VERBOSE)
Without the verbose setting, this RE would look like:
charref = re.compile("&#([0-9]+[^0-9]|0[0-7]+[^0-7]|x[0-9a-fA-F]+[^0-9a-fA-F])")
This nice example, together with a complete introduction to Python's regular expressions, was written by A.M. Kuchling. [amk]
From the Python Library Reference:
12.20 csv -- CSV File Reading and Writing
New in version 2.3.
The so-called CSV (Comma Separated Values) format is the most common import and export format for spreadsheets and databases. There is no "CSV standard", so the format is operationally defined by the many applications which read and write it. The lack of a standard means that subtle differences often exist in the data produced and consumed by different applications. These differences can make it annoying to process CSV files from multiple sources. Still, while the delimiters and quoting characters vary, the overall format is similar enough that it is possible to write a single module which can efficiently manipulate such data, hiding the details of reading and writing the data from the programmer.
The csv module implements classes to read and write tabular data in CSV format. It allows programmers to say, "write this data in the format preferred by Excel," or "read data from this file which was generated by Excel," without knowing the precise details of the CSV format used by Excel. Programmers can also describe the CSV formats understood by other applications or define their own special-purpose CSV formats.
Based on Simon Brunning's "Python Quick Reference" [pqr]
Generators, new in Python 2.2, are a powerful construct for managing control flow. In cases where they are useful, such as gathering and subtotalling several members of an input file, the errors they avoid will leave you in tears for time wasted in your youth using lesser tools.
Too large a topic for a first evening's introduction, generators are nevertheless documented fully in the distribution.
Also, an article by David Mertz, Ph.D., at
http://gnosis.cx/publish/programming/charming_python_b1.html
provides a a good introduction.
None of these is necessarily a big deal, but they may be "significantly illuminating" (tm).
In a Python class I taught last December the question came up, "Does Python pass by value or by reference?"
The question stymied me. I tried "by value" and came up with contradictions and counterexamples. I tried "by reference" with the same result.
The correct answer is that the question is phrased in terms of other languages, and doesn't really have meaning for Python. What's important is binding of names, and namespaces, and mutability of objects. A different design philosophy.
Good material for another evening's Python talk.
OO languages or at least a few of them (e.g., C++) have given OO a bad rap. It is not OO itself which is large and kludgy, it's the implementation.
You don't need to be put off by the fact that Python is object oriented from its foundations up, for two reasons.
Python's "Dictionary" mapping data type was one of the things I was slow to catch on to.
Integers and strings seemed obvious.
Tuples and lists took a little work, but they became obvious quickly.
I left dictionaries until last, probably because they seemed complicated and frought with overhead.
This was a mistake, and a beginner is well counseled to make the acquaintance of dictionaries early on. They are immensely powerful. They are also highly optimized, and are used as the backbone of Python's internals. (Namespaces are implemented as dictionaries, for example.) See the Python Cookbook for a discussion.
Python's performance varies with the application.
Some things are slow, and some things can be implemented to run faster using a low-level language.
But I've used Python for years, and only once had to change code to improve performance. And even then the fix was to write smarter Python code, not resort to assembly language.
Actually, I've never heard of a project which failed to deliver the goods because of performance issues. I've heard of many projects which failed because the development couldn't get done in time, however.
I hope this tiny selection of Python topics has illustrated Python's productivity, pleasure of use, subsequent maintainability, and incredible rapidity.
I hope it has whetted your appetite to make a better acquaintance of Python.
--
by Tim Peters [PetersZ]
Edsger W. Dijkstra [EWD594]
(Recently I found the following text in manuscript among old papers of mine. It must have been written in the middle of 1973, but I don't think that in the intervening three years it has lost anything of its significance. Hence I now incorporate it in the EWD-series.)
A parable.
Years ago a railway company was erected and one of its directors --probably the commercial bloke-- discovered that the initial investments could be reduced significantly if only fifty percent of the cars would be equipped with a toilet, and, therefore, so was decided.
Shortly after the company had started its operations, however, complaints about the toilets came pouring in. An investigation was carried out and revealed that the obvious thing had happened: despite its youth the company was already suffering from internal communication problems, for the director's decision on the toilets had not been transmitted to the shunting yard, where all cars were treated as equivalent, and, as a result, sometimes trains were composed with hardly any toilets at all.
In order to solve the problem, a bit of information was associated with each car, telling whether it was a car with or without a toilet, and the shunting yard was instructed to compose trains with the numbers of cars of both types as equal as possible. It was a complication for the shunting yard, but, once it had been solved, the people responsible for the shunting procedures were quite proud that they could manage it.
When the new shunting procedures had been made effective, however, complaints about the toilets continued. A new investigation was carried out and then it transpired that, although in each train about half the cars had indeed toilets, sometimes trains were composed with nearly all toilets in one half of the train. In order to remedy the situation, new instructions were issued, prescribing that cars with and cars without toilets should alternate. This was a move severe complication for the shunting people, but after some initial graumbling [sic], eventually they managed.
Complaints, however, continued and the reason turned out to be that, as the cars with toilets had their toilet at one of their ends, the distance between two successive toilets in the train could still be nearly three car lengths, and for mothers with children in urgent need --and perhaps even luggage piled up in the corridors-- this still could lead to disasters. As a result, the cars with toilets got another bit of information attached to them, making them into directed objects, and the new instructions were, that in each trains [sic] the cars with toilets should have the same orientation. This time, the new instructions for the shunting yard were received with less than enthusiasm, for the number of truntables [sic] was hardly sufficient; to be quite fair to the shunting people we must even admit that according to all reasonable standards, the number of turntables was insufficient, and it was only by virtue of the most cunning ingenuity, that they could just manage.
With all toilets equally spaced along the train the company felt confident that now everything was alright, but passengers continued to complain: although no passenger was more than a car length away from the nearest toilet, passengers (in urgent need) did not know in which direction to start their stumbling itinary [sic] along the corridor! To solve this problem, arrows saying "TOILET" were fixed in all corridors, thereby also making the other half of the cars into directed objects that should be properly oriented by the shunting procedure.
When the new instruction reached the shunting yard, they created an atmosphere ranging from despair to revolt: it just couln't [sic] be done! At that critical moment a man whose name has been forgotten and shall never be traced, made the following observation. When each car with a toilet was coupled, from now until eternity, at its toileted end with a car without a toilet, from then onwards the shunting yard, instead of dealing with N directed cars of two types, could deal with N/2 identical units that, to all intents and purposes, could be regarded as symmetrical. And this observation solved all shunting problems at the modest price of, firstly sticking to trains with an even number of cars only --the few additional cars needed for that could be paid out of the initial savings effected by the commercial bloke!-- and, secondly, slightly cheating with regard to the equal spacing of the toilets. But, after all, who cares about the last three feet?
Although at the time that this story took place, mankind was not blessed yet with automatic computers, our anonymous man who found this solution deserves to be called the world's first competent programmer.
--
I have told the above story to different audiences. Programmers, as a rule, are delighted by it, and managers, invariably, get more and more annoyed as the story progresses; true mathematicians, however, fail to see the point.
Platasnstreat 5 prof.dr.Edsger W. Dijkstra NL-4565 NUENEN Burroughs Research Fellow The Netherlands
For further reading, these are Bill's selections made as though the list could contain only a few entries. (There are many, many good Python references.) This tiny list emphasizes one entry each from a physical book and from the distro or the 'net.
The "Fount of All Things Python" is: http://www.python.org/
Personal credits for the stuff in this presentation:
References:
| [EWD594] | A Parable, Dijkstra, http://www.cbi.umn.edu/collections/inv/burros/ewd594.htm |
| [Eckels01] | From Conversations: It Fits Your Brain: the Ninth Annual International Python Conference, covered by Mike Orr in Linux Journal, April 25, 2001; http://www.linuxjournal.com/article.php?sid=4731 |
| [GvR96] | Guido van Rossum, http://www.python.org/doc/essays/foreword.html |
| [amk] | A.M. Kuchling, Regular Expression Howtwo, http://www.amk.ca/python/howto/regex/ |
| [Mattison86] | Snakes of the World, Chapter 8, "Snakes and Man"; Christopher Mattison, Facts On File, Inc, New York, 1986, ISBN --8160-1082-X |
| [PetersZ] | The Zen of Python, by Tim Peters; http://www.python.org/doc/Humor.html#zen |
| [leo] | The Leo outlining editor, http://personalpages.tds.net/~edream/front.html, http://sourceforge.net/projects/leo |
| [pqr] | Python Quick Reference (for version 2.1), by Simon Brunning, http://www.brunningonline.net/simon/python/PQR.html |
| [pycomps] | http://www.python.org/doc/Comparisons.html |
| [pysum] | (1, 2) http://www.python.org/doc/Summary.html |
| [SciTE] | (1, 2) http://www.scintilla.org/SciTE.html |