Q:

'leet' program - getting all permutations

I wrote a straightforward function that converts letters in words to their 'leet' numerical counterparts.
def Leet(word):
    letters = list(word.lower())
    for n, letter, in enumerate(letters):
        if letter == 'o':
            letters[n]= '0'
        elif letter == 'i':
            letters[n]= '1'
        elif letter == 'z':
            letters[n]= '2'
        elif letter == 'e':
            letters[n]= '3'
        elif letter == 'a':
            letters[n]= '4'
        elif letter == 's':
            letters[n]= '5'
        elif letter == 'g':
            letters[n]= '6'
        elif letter == 't':
            letters[n]= '7'
        elif letter == 'b':
            letters[n]= '8'
    return ''.join(letters)

so when I input 'zit', the program will return '217'. 
My question is, how can I change it to give me every possible permutation ('217', '2it', 'z1t', 'zi7', '21t', etc.)? I've read about itertools but I am stumped as to how to apply it to my function.

A:

First observation is that you can shorten the lookup, like this:
REPLACE = { letter: str(index) for index, letter in enumerate('oizeasgtb') }

def Leet2(word):
    letters = [ REPLACE.get(l, l) for l in word.lower() ]
    return ''.join(letters)

REPLACE looks like:
{'a': '4', 'b': '8', 'e': '3', 'g': '6', 'i': '1', 
 'o': '0', 's': '5', 't': '7', 'z': '2'}

And the REPLACE.get(l,l) gives you back either the replacement letter, or the original letter if there is no replacement.
The second observation is that you don't really want permutations, which are shifts in ordering. The permutations of '217' are:
>>> [ ''.join(p) for p in permutations('217') ]
['217', '271', '127', '172', '721', '712']

What you really need is the product of a list that encodes all the possible choices for a given character position:
[('z', '2'), ('i', '1'), ('t', '7')]

How this works might be clearer if I also show a possibility list with some characters for which there is no valid replacement. For 'red' for example:
[('r',), ('e', '3'), ('d',)]

Now we need the string-joined product of those options. Putting it all together:
from itertools import product

def Leet2Combos(word):
    possibles = []
    for l in word.lower():
        ll = REPLACE.get(l, l)
        possibles.append( (l,) if ll == l else (l, ll) )
    return [ ''.join(t) for t in product(*possibles) ]

print Leet2Combos('zit')
print Leet2Combos('red')

Gives:
['zit', 'zi7', 'z1t', 'z17', '2it', '2i7', '21t', '217']
['red', 'r3d']

A:

Use itertools.product. Also, I would suggest using a dict for the mapping instead of a cascade of if/elif.
>>> from itertools import product
>>> LEET = { 'z': '2', 'i': '1', 't': '7' } # and all the others     
>>> word = "zit"
>>> [''.join(letters) for letters in product(*({c, LEET.get(c, c)} for c in word))]
['zit', 'zi7', 'z1t', 'z17', '2it', '2i7', '21t', '217']

Note that LEET.get(c, c) will get the "leet" letter from the dict, or use the original letter as a default. The {...} is used to make those pairs sets, so there are no duplicates for letters with no replacement. In older versions of Python, you might have to use set([...]) instead.
That rather complex product(*...) line works roughly like this:
    product(*({c, LEET.get(c, c)} for c in 'zit'))
==> product(*({'z', LEET.get('z', 'z')}, {'i', LEET.get('i', 'i')}, {'t', LEET.get('t', 't')}))
==> product(*({'z', '2'}, {'i', '1'}, {'t', '7'}))
==> product(  {'z', '2'}, {'i', '1'}, {'t', '7'} )

which produces the cartesian product of all those letters and their replacements.

