This post originated from an RSS feed registered with Python Buzz
by Thomas Guest.
Original Post: Permission and Forgiveness
Feed Title: Word Aligned: Category Python
Feed URL: http://feeds.feedburner.com/WordAlignedCategoryPython
Feed Description: Dynamic languages in general. Python in particular. The adventures of a space sensitive programmer.
I needed to check the times for trains from Bristol to Paddington, so
I went to http://thetrainline.com and
filled out the Quick Timetable form, entering bristol temple meads
and paddington as my departure and destination points.
Which Paddington?
As you can see, I stick to lowercase when I’m in a hurry – but I
did remember to specify which of the Bristol mainline stations I
intended to leave from, knowing from previous experience that the
Quick Timetable wouldn’t be smart enough to simultaneously provide
answers for both.
Unfortunately the trainline software couldn’t figure out which
Paddington I wanted to travel to. What’s worse, it tried to cover up
its confusion with a plethora of information and options. Sure, a
single click on the LONDON PADDINGTON link was all I needed to do,
but that link was buried half-way down the page, after a text entry field and
an alphabet selector.
As you can see, the trainline uses uppercase for stations, which isn’t
reader-friendly.
London Paddington has to be the best known station in the
country. It’s also the only mainline Paddington station. It’s also in
exactly the same place as Paddington Underground. Couldn’t the
Trainline have made a wild guess that when I said Paddington I meant
LONDON PADDINGTON?
By this stage, I was curious to find out what would happen if I asked
for train times from Bristol Temple Meads to Paddington Underground. I
got a page full of unhelpful apologies, starting with:
We cannot find any services that meet your request.
Possible reasons and solutions are listed below. You can try and search for another journey by clicking on the back button:
State of the Art
The trainline isn’t an egregious website. As you can see, I
prefer it to National Rail Enquiries
for timetable queries. But that’s no excuse. The
trainline should have figured out which Paddington I meant without
needing my permission to do so and begged my forgiveness if it
misinterpreted me.
Just like a good email client should go ahead and delete an
email when I ask it to, rather than present an irritating confirmation
dialogue asking if I really am really sure I really want to really
delete it – so I end up OK’ing the confirmation dialogue
automatically, meaning that the one time it might have saved me, it
doesn’t. A good email client just deletes the message without
further permission and provides a way for me to recover it if
necessary.
It’s sometimes worth reminding ourselves that computers are here to
serve us. As programmers, perhaps we’re rather too used to wrestling
them into submission – perhaps we sometimes secretly enjoy it – and as a
consequence we expect other users to tolerate such impudence.
Being a programmer, of course I managed to get the train times. I then
found myself thinking about a common Python idiom for handling tricky
inputs gracefully.
Permission and Forgiveness
Code written in a statically typed language – even if that language
supports exceptions – often gets swamped by error handling
code. There are more ways for things to go wrong than right, and they
all need handling.
Python is a dynamic language. Doesn’t that mean that there are even
more ways to go wrong? After all, the compiler isn’t going to check
that we pass a nice string into the following integer conversion
function.
This function is woefully under-specified and guaranteed to misfire in
the face of poor input, but with a little more thought it can go
right in situations which it wasn’t originally coded for. Here’s an
improved version:
better number converter
defthing_to_int(thing,default=0):""" Converts the input thing into an integer, returning a
default value if the conversion fails.
>>> thing_to_int("10")
10
>>> assert thing_to_int(1.e12) == 1000000000000
>>> thing_to_int("one")
0
>>> thing_to_int("one", default=-1)
-1
>>> class forty_two(object):
... def __int__(self): return 42
>>> assert thing_to_int(forty_two()) == 42
"""try:returnint(thing)except:returndefaultdef_test():importdoctestdoctest.testmod()if__name__=="__main__":_test()
The function’s documentation shows just how flexible this simple
function is. The idiom used here is to proceed assuming that the
function caller has passed a suitable thing parameter, but to handle
any errors which occur.
Here’s another example, showing how we can use set in a version of
Python which didn’t build support for sets into the core
language. Note that we don’t attempt to query version numbers or check
which libraries have been installed. We just try and use set and
handle any fall-out.