[Tutor] please return flys in ointment

Steven D'Aprano steve at pearwood.info
Mon Jul 8 09:12:15 CEST 2013


Comments on your code inline below.

On Sat, Jul 06, 2013 at 02:38:27PM -0700, Jim Mooney wrote:


> import sys
> 
> # Data
> ones = {'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five',
> '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine'}
> 
> tens = {'2': 'twenty', '3': 'thirty', '4': 'forty', '5': 'fifty', '6':
> 'sixty', '7': 'seventy', '8': 'eighty', '9': 'ninety'}
> 
> doubles = {'0': 'ten', '1': 'eleven', '2': 'twelve', '3': 'thirteen', '4':
> 'fourteen', '5': 'fifteen', '6': 'sixteen', '7': 'seventeen', '8': 
> 'eighteen', '9': 'nineteen'}
> 
> powers_of_1000 = (' thousand', ' million', ' billion', ' trillion',
> ' quadrillion', ' quintillion', ' sextillion', ' septillion', ' octillion',
> ' nonillion', ' decillion')
> 
> '''add these later, and also option for dollars-and-cents ending.
> 'vigintillion', 'novemdecillion', 'octodecillion', 'septendecillion',
> 'sexdecillion', 'quindecillion', 'quattuordecillion', 'tredecillion',
> 'duodecillion', 'undecillion',
> 'decillion', 'nonillion'
> '''
> 
> # Functions
> def make_triplets_from_input():
>     '''Enter a positive integer. A list of triplets in the same order will
> be returned.
>     Raise ValueError to loop on non-integer input, or return 'zero'
> trigger-value of
>     'xxx' if all zeroes are entered. If input is okay, strip leading
> zeroes, then
>     zero-pad leftmost triplet to three characters, so all slices are
> triplets. Adjust
>     input for different Python versions.'''
>     while True:
>         try:
>             if sys.version[0:3] == '2.7':
>                 numbers_str = original = raw_input('Enter a positive
> integer, space \
> separated if desired.')
>             elif sys.version[0:3] == '3.3':
>                 numbers_str = original = input('Enter a positive integer,
> space \
> separated if desired.')

A better way to handle this is to perform a check once, outside the 
function at the top of the file:

try:
    raw_input
except NameError:
    # Probably Python 3.
    raw_input = input

and then just use raw_input inside the function. Or the other way 
around, if you prefer:

try:
    raw_input
except NameError:
    pass
else:
    input = raw_input

Then, inside the function, just unconditionally call:

    result = input("Prompt, or 'quit' to exit: ")
    result = result.lower().strip()
    if result == 'quit':
        break



>             else:
>                 print('Python versions 2.7 and 3.3 only supported')
>                 sys.exit()
> 
>             numbers_str = ''.join(numbers_str.split())
>             if numbers_str == '' or numbers_str == ' ': raise ValueError

Since you're splitting on whitespace, and joining with the empty string, 
you cannot get numbers_str == ' '.

Besides, no need to explicitly check for '' here, since the very next 
line will do so for you:

>             numbers_int = int(numbers_str)
>             if numbers_int == 0:
>                 print('Zero')
>                 sys.exit()

One should not treat "0" to mean "I want to quit".

>             numbers_str = str(numbers_int)
>             if len(numbers_str) > 36: raise ArithmeticError

ArithmeticError??? You're not doing arithmetic, how can this be an 
arithmetic error?

You should be able to handle implausibly/arbitrarily long numbers, just 
by chaining names (e.g. "billion billion billion billion..."). If you're 
going to force some arbitrary limit on the number, and then use an 
exception for flow-control (as you do here, catching ArithmeticError 
further on), please use a custom exception so as to not confuse the 
person reading your code. 


>             break
>         except KeyboardInterrupt:
>             print('Program cancelled by user')
>             sys.exit()

/me lip curls

I'm not sure I like that... 


>         except ValueError as err:
>             print(original, "is not an integer.")
>             continue
>         except ArithmeticError as err:
>             print(original, "is too big.\n999...decillion is max: 10**37-1 or 36 chars \
> or 12 groups of 3 chars")
> 
>     leftpad = len(numbers_str) % 3 # zero-pad left, so we get all 3-character triplets
>     if leftpad == 0: leftpad = ''
>     elif leftpad == 2: leftpad = '0'
>     else: leftpad = '00'
>     numbers_str = leftpad + numbers_str

    leftpad = len(numbers_str) % 3
    if leftpad != 0:
        leftpad = 3-leftpad
    numbers_str = '0'*leftpad + numbers_str



>     triplets = [numbers_str[x:x+3] for x in range(0,len(numbers_str),3)]
>     return (triplets, len(triplets))

See also the recipe for "grouper" in the documentation for itertools.



-- 
Steven


More information about the Tutor mailing list