Project Euler #17: Number Letter Counts
num_to_alpha =\ is not Pythonic.  If a statement is “incomplete”, there is no need to add a trailing \ to indicate the line is continued.  An “incomplete” statement is one which contains unclosed {, [, or (, so this line could be written without the trailing \ simply by moving the { up to the previous line:

num_to_alpha = {
    1: 'one', ...


The if 0 &lt; n &lt; 21 or n == 100 or n == 1000: test could be written more simply as if n in num_to_alpha:.  This would also handle many other simple cases where the number exists in the num_to_alpha dictionary.

You compute mod = n % 10, and then go on to test if n % 10 != 0:.  You could simply test the already computed value if mod != 0:, or since non-zero values are Truthy, if mod:.

You are testing for values which cannot possibly be true due to early tests.  For instance, once a number is above 100, you test:

if n % 100 == 0:
    return ...


This is followed by:

if not n % 100 == 0 and ...:


If n % 100 evaluated to True, the first return statement would have been executed.  Testing not n % 100 == 0 doesn’t add any value.

At the end of the function, you explicitly check if n &lt;= 0 or n &gt; 1000: and return the empty string.  And if THAT test fails ... what happens?  None will be returned?  If the number passed any of the above tests, and explicit return statement would have already returned the desired string, so if the end of the function is reached, does it matter what the return value is?  Could you not unconditionally return ''.  Or better: raise ValueError()



DRY: Don’t Repeat Yourself

You code is repeating the same tests.  Is the number between 1 and 20?  Are the last 2 digits of a 3 digit number between 1 and 20?

If you structured your function in the following manner, you’d repeat yourself less, and find it easy to extend into ten thousands, hundred thousands, millions and beyond:


Start with an empty string
If n &gt;= 1000


add num_to_alpha[n // 1000] and “thousand” to the string,
n = n % 1000

If n &gt;= 100


add num_to_alpha[n // 100] and “hundred” to the string,
n = n % 100

If n &gt; 0 and the string is not empty,


add “and” to the string

If n &gt; 20,


add num_to_alpha[n // 10 * 10] to the string
n = n % 10

If n &gt; 0


add num_to_alpha[n] to the string

Return the resulting string




Since you aren’t really using the returned strings, but eventually just counting the number of letters in the resulting string, you could optimize the function to not use strings at all.  Just store the number of letters of each number in the dictionary:

num_to_length = {
    1: 3, 2: 3, 3: 5, 4: 4, ...
}


and add 8, 7, and 3 instead of the strings “thousand”, “hundred” &amp; “and”.