Project Euler no. 17: Counting letters to write the numbers from 1 to 1000
Aside from the repetitive logic in the code to handle numbers greater than a hundred, the solution is not bad.  There are many observations that could lead to improvements, though.


You don't need to build the strings themselves; you just need to sum their lengths.  In particular, since strings are immutable, += is relatively costly.  (Your solution is still fast enough, though.)
The challenge never requires you to treat 0 as "zero".  You can therefore simplify some logic by mapping 0 to an empty string.
The ranges 0 &lt; num &lt;= 20, 21 &lt;= num &lt;= 99, 100 &lt;= num &lt;= 999, and num == 1000 are mutually exclusive.
You can reduce repetition by using recursion.


Solution

numbers_dict = {
    n: len(word) for (n, word) in {
        0: "",
        1: "one",
        2: "two",
        3: "three",
        4: "four",
        5: "five",
        6: "six",
        7: "seven",
        8: "eight",
        9: "nine",
        10: "ten",
        11: "eleven",
        # ...
        90: "ninety",
    }.iteritems()
}

def num_length(n):
    if n &lt;= 20:
        return numbers_dict[n]
    elif n &lt; 100:
        ones = divmod(n, 10)
        return numbers_dict[10 * tens] + num_length(rest)
    elif n &lt; 1000:
        hundreds, rest = divmod(n, 100)
        return num_length(hundreds) + len("hundred") + (
            len("and") + num_length(rest) if rest else 0
        )
    else:
        thousands, rest = divmod(n, 1000)
        return num_length(thousands) + len("thousand") + num_length(rest)

print(sum(num_length(n) for n in range(1, 1001)))


You could also use numbers_dict for memoization:

def num_length(n):
    if n in numbers_dict:
        return numbers_dict[n]
    elif n &lt; 100:
        tens, rest = divmod(n, 10)
        numbers_dict[n] = numbers_dict[10 * tens] + num_length(rest)
        return numbers_dict[n]
    elif n &lt; 1000:
        hundreds, rest = divmod(n, 100)
        return num_length(hundreds) + len("hundred") + (
            len("and") + num_length(rest) if rest else 0
        )
    else:
        thousands, rest = divmod(n, 1000)
        return num_length(thousands) + len("thousand") + num_length(rest)