Why Python programmers should learn Python
I recently clicked upon Keith Braithwaite and Ivan Moore’s presentation, “Why Java Programmers Should Learn Python”. It starts off well with an expert discussion of three different axes of type systems, placing various programming languages in the resulting 3-space. It then poses a programming problem, the kind of problem which Python excels at:
Given the supplied library classes write some code in “quickSilver” to convert between word and decimal digits representations of positive integers less than 1001.
e.g. “five seven three” → 573
e.g. 672 → “six seven two”
The Java programmers attending the presentation don’t know it yet, but “quickSilver” turns out to be Python, or at least a subset of it sufficient to solve the problem, and the final slides of the presentation contain a model solution to this problem.
A “model” solution?
Here is that solution.
class WordsFromNumber: def __init__(self,number): self.representationOf = number def wordsOf(self): lsd = self.representationOf % 10 msds = self.representationOf / 10 if msds == 0: return self.wordFor(self.representationOf) else: return WordsFromNumber(msds).wordsOf() + " " + \ self.wordFor(lsd) def wordFor(self,numeral): return ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"][numeral] class NumberFromWords: def __init__(self,words): self.representation = words def number(self): words = split("\\s",self.representation) return self.unpack(words) def unpack(self,words): if len(words) == 1: return self.numberFor(words[0]) else: lswi = len(words) - 1 lswi = words[lswi] msws = words[0:lswi] return self.numberFor(lsw) + 10 * self.unpack(msw) def numberFor(self,word): return {"zero" : 0, "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5, "six" : 6, "seven" : 7, "eight" : 8, "nine" : 9}[word]
I don’t know what point the presenters were trying to make. I wasn’t in their audience but if I were, this code wouldn’t go any way towards persuading me I should bother with Python. It’s got two classes where none is needed, it uses recursion when container operations would do, it’s longer than it needs to be, and (putting myself in a Java programmer’s shoes) are all those self
s really needed?
In other words this, to me, looks like Java written in Python. I’ll assume this is the point the presenters are trying to make, but if they revise the presentation, I’d like to suggest extending it to add an alternative ending, which I think shows off Python’s advantages.
number_to_word = { '0' : 'zero', '1' : 'one', '2' : 'two', '3' : 'three', '4' : 'four', '5' : 'five', '6' : 'six', '7' : 'seven', '8': 'eight', '9' : 'nine' } word_to_number = { 'zero' : 0, 'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4, 'five' : 5, 'six' : 6, 'seven' : 7, 'eight' : 8, 'nine' : 9 } def number_to_words(number): return ' '.join(number_to_word[c] for c in str(number)) def words_to_number(words): def dec_shift_add(x, y): return x * 10 + y return reduce(dec_shift_add, (word_to_number[w] for w in words.split()))
I’ve omitted documentation in order to squeeze the encode-decode functions on to a single slide. If I had time and space, though, I’d show how to document and test this function in one go using doctest
, taking care to cover what happens with invalid inputs.
By the way, you’ll see I’m using reduce
while I can! I think this example is one of those rare cases where it works well.
Dictionary initialisation
The explicit dictionaries are fine and good and almost certainly what should be presented to an audience of Python virgins, but in my own code I’d be tempted to remove a little replication. (My thanks to Ashwin for tactfully pointing out a howling bug in the code originally posted here).
import string words = "zero one two three four five six seven eight nine".split() number_to_word = dict(zip(string.digits, words)) word_to_number = dict(zip(words, range(10)))
Living by the Sword
A recent (2008-03-07) wave of hits from Reddit has prompted me to revisit this note and, cough, refactor my code. There’s no need for any mathematics, nested functions or reduce
: this problem is better handled by string conversions. And the goal of squeezing functions on a single slide is misguided. This code needs doctesting. Java programmers are used to good test frameworks with great tool support, but Python excels at this too.
from string import digits words = 'zero one two three four five six seven eight nine'.split() number_to_word = dict(zip(digits, words)) word_to_number = dict(zip(words, digits)) def number_to_words(number): '''Converts a positive integer into a string of digit names. Examples: >>> number_to_words(123) 'one two three' >>> number_to_words(-1) Traceback (most recent call last): ... KeyError: '-' ''' return ' '.join(number_to_word[c] for c in str(number)) def words_to_number(words): '''Converts a string of digit names into an integer. Examples: >>> words_to_number('one two three') 123 >>> words_to_number('One tWo thrEE') 123 >>> words_to_number('zero one two') 12 >>> words_to_number('minus one') Traceback (most recent call last): ... KeyError: 'minus' >>> all(words_to_number(number_to_words(x)) == x for x in range(100)) True ''' return int(''.join(word_to_number[w] for w in words.lower().split())) if __name__ == "__main__": import doctest doctest.testmod()
Feedback
-
An alternative for words_to_number which may or may not be better:
... word_to_number = dict(zip(words, string.digits)) ... def words_to_number(words): int(''.join([word_to_number[x] for x in words.split()]))
-
Thanks SpComb, I think I do prefer your version. It's more symmetrical.
-
Hi Thomas, Thanks for your comments. I was a long time ago that we did that session, and we would probably do it differently today.
The goal really wasn't to show off any particular features of Python (despite the title)--we'd have preferred to use Smalltalk or Self anyway, but Python seemed to us the least threatening dynamic OO language at the time. The goal was only to fairly gently open some folk's eyes to what happens when you start to move away from the straightjacket of Java.
I dimly recall that we didn't want to "frighten the horses" too much, which would have been easy: for example, one attendee claimed quite strenuously that there was no advantage to a language having more abstract constructs in it for traversing a collection than the for-loop because for-loop code just flows out of your fingers so easily you hardly notice.
Keith