Numbers

"""
translate - translates number words to digits

Example Usage:

.. code-block:: bash

    $ translate.py one two three
    123
    $ translate.py zero zero five zero six zero
    5060
    $ translate.py blah two blah four
    2 4

Any leading "zero" arguments are ignored.
Invalid digit words are converted to spaces.

"""
import sys


def translate_numbers(*number_strings):
    numbers = []
    for item in zip(range(len(number_strings)), number_strings):
        i,n = item
        if n == "zero":
            if i != 0:
                numbers.append(0)
        elif n == "one":
            numbers.append(1)
        elif n == "two":
            numbers.append(2)
        elif n == "three":
            numbers.append(3)
        elif n == "four":
            numbers.append(4)
        elif n == "five":
            numbers.append(5)
        elif n == "six":
            numbers.append(6)
        elif n == "seven":
            numbers.append(7)
        elif n == "eight":
            numbers.append(8)
        elif n == "nine":
            numbers.append(9)
        else:
            numbers.append(' ')
    return numbers


def main(numbers):
    number_list = numbers
    numbers = translate_numbers(*number_list)
    numbers = map(str, numbers)
    print("".join(numbers))


if __name__ == "__main__":
    main(sys.argv[1:])

Let’s tackle the main function first:

  1. Remove unnecessary number_list variable by combining first two lines: “Simple is better than complex.”

  2. Use more clear variable name instead of re-using old variable name: PEP 8 and “Readability counts.”

def main(numbers):
    translation = map(str, translate_numbers(*numbers))
    print("".join(translation))

Remove unnecessary translation variable: “Beautiful is better than ugly.”

def main(numbers):
    print("".join(map(str, translate_numbers(*numbers))))

Now let’s work on translate_numbers:

  1. Use enumerate: “Complex is better than complicated.”

  2. Do item unpacking right in for loop: “Simple is better than complex.”

  3. Do append one-time only: “Simple is better than complex.” and “Readability counts.”

def translate_numbers(*number_strings):
    numbers = []
    for i, n in enumerate(number_strings):
        x = None
        if n == "zero":
            if i != 0:
                x = 0
        elif n == "one":
            x = 1
        elif n == "two":
            x = 2
        elif n == "three":
            x = 3
        elif n == "four":
            x = 4
        elif n == "five":
            x = 5
        elif n == "six":
            x = 6
        elif n == "seven":
            x = 7
        elif n == "eight":
            x = 8
        elif n == "nine":
            x = 9
        else:
            x = ' '
        if x is not None:
            numbers.append(x)
    return numbers
  1. Put string to number translations in dictionary: “Explicit is better than implicit.”

  2. Collapse most of the logic to a dictionary item lookup, catching key errors: PEP 8 and “Errors should never pass silently.”

  3. Use double quotes more consistently: PEP 8 and “Readability counts.”

def translate_numbers(*number_strings):
    translation = {
        "one": 1,
        "two": 2,
        "three": 3,
        "four": 4,
        "five": 5,
        "six": 6,
        "seven": 7,
        "eight": 8,
        "nine": 9,
    }
    numbers = []
    for i, n in enumerate(number_strings):
        x = None
        if n == "zero":
            if i != 0:
                x = 0
        else:
            try:
                x = translation[n]
            except KeyError:
                x = " "
        if x is not None:
            numbers.append(x)
    return numbers
  1. Add zero to translation dictionary: “Explicit is better than implicit.” and “Flat is better than nested.”

  2. Invert nested-if special case and move x = None inside if statement: “Complex is better than complicated.”

  3. Use dictionary’s get method to use a default on key errors: “Explicit is better than implicit.”

  4. Use single quotes for dictionary key strings (personal preference)

def translate_numbers(*number_strings):
    translation = {
        'zero': 0,
        'one': 1,
        'two': 2,
        'three': 3,
        'four': 4,
        'five': 5,
        'six': 6,
        'seven': 7,
        'eight': 8,
        'nine': 9,
    }
    numbers = []
    for i, n in enumerate(number_strings):
        if n == "zero" and i == 0:
            x = None
        else:
            x = translation.get(n, " ")
        if x is not None:
            numbers.append(x)
    return numbers
  1. Invert zero special case logic entirely and collapse if and else: “Simple is better than complex.”

  2. Remove last x is None check and just do append all at once: “Simple is better than complex.”

  3. Remove unnecessary x and just append immediately: “Beautiful is better than ugly.”

def translate_numbers(*number_strings):
    translation = {
        'zero': 0,
        'one': 1,
        'two': 2,
        'three': 3,
        'four': 4,
        'five': 5,
        'six': 6,
        'seven': 7,
        'eight': 8,
        'nine': 9,
    }
    numbers = []
    for i, n in enumerate(number_strings):
        if not (n == "zero" and i == 0):
            numbers.append(translation.get(n, " "))
    return numbers
  1. Move translation into global variable (capitalized to denote constant): PEP 8

  2. Convert for loop into a list comprehension: “Complex is better than complicated.”

  3. Remove unnecessary numbers variable which is returned immediately: “Beautiful is better than ugly.”

  4. Use DeMorgan’s law to flip boolean logic in condition: “Simple is better than complex.”

NUMBERS = {
    'zero': 0,
    'one': 1,
    'two': 2,
    'three': 3,
    'four': 4,
    'five': 5,
    'six': 6,
    'seven': 7,
    'eight': 8,
    'nine': 9,
}


def translate_numbers(*number_strings):
    return [
        NUMBERS.get(n, " ")
        for i, n in enumerate(number_strings)
        if i != 0 or n != "zero"
    ]