Translating chars in a string
I've seen this situation pop up a few times, so I thought a tip would be good.
Suppose you have a string s and you want to translate some chars of s to other chars (think ROT-13 like ciphers). For a more concrete example, suppose we want to swap just the as and bs in a string, e.g.
"abacus" -> "babcus"
The naïve way to do this would be:
lambda s:s.replace('a','T').replace('b','a').replace('T','b')
Note how we need to introduce a temporary 'T' to get the swapping right.
With eval, we can shorten this a bit:
lambda s:eval("s"+".replace('%s','%s')"*3%tuple("aTbaTb"))
For this particular example, iterating char-by-char gives a slightly better solution (feel free to try it!). But even so, the winner is str.translate, which takes a dictionary of from: to code points:
# Note: 97 is 'a' and 98 is 'b'
lambda s:s.translate({97:98,98:97})
In Python 2 this only works for Unicode strings, so unfortunately the code here is slightly longer:
lambda s:(u''+s).translate({97:98,98:97})
Some important points which make str.translate so useful are:
It's easily extendable.
It's easily extendable.Any char not specified is untouched by default, e.g. the
Any char not specified is untouched by default, e.g. the"cus"in"abacus"above."cus"in"abacus"above.The
topart of the dictionary can actually be a (Unicode) string orNoneas well.- For the former case,
{97:"XYZ"}(u"XYZ"in Python 2) would turnabacus -> XYZbXYZcus. - For the latter case, mapping to
Nonedrops the char, so{97:None}givesabacus -> bcus.
topart of the dictionary can actually be a (Unicode) string as well, e.g.{97:"XYZ"}(u"XYZ"in Python 2) would turnabacus -> XYZbXYZcus. It can also beNone, but that doesn't save any bytes compared to""oru"".- For the former case,