Recipe 1.9. Simplifying Usage of Strings' translate Method
Credit: Chris Perkins, Raymond Hettinger
Problem
You often want to use the fast code in
strings' TRanslate method, but
find it hard to remember in detail how that method and the function
string.maketrans work, so you want a handy
facade to simplify their use in typical cases.
Solution
The TRanslate method of strings is quite powerful
and flexible, as detailed in Recipe 1.10. However, exactly because of
that power and flexibility, it may be a nice idea to front it with a
"facade" that simplifies its
typical use. A little factory function,
returning a closure, can do wonders for this kind of
task:
import string
def translator(frm='', to='', delete='', keep=None):
if len(to) == 1:
to = to * len(frm)
trans = string.maketrans(frm, to)
if keep is not None:
allchars = string.maketrans('', '')
delete = allchars.translate(allchars, keep.translate(allchars, delete))
def translate(s):
return s.translate(trans, delete)
return translate
Discussion
I often find myself wanting to use strings'
translate method for any one of a few purposes,
but each time I have to stop and think about the details (see Recipe 1.10 for more information
about those details). So, I wrote myself a class (later remade into
the factory closure presented in this recipe's
Solution) to encapsulate various possibilities behind a
simpler-to-use facade. Now, when I want a function that keeps only
characters from a given set, I can easily build and use that
function:
>>> digits_only = translator(keep=string.digits)It's similarly simple when I want to
>>> digits_only('Chris Perkins : 224-7992')
'2247992'
remove a set of characters:
>>> no_digits = translator(delete=string.digits)and when I want to replace a set of characters with a single
>>> no_digits('Chris Perkins : 224-7992')
'Chris Perkins : -'
character:
>>> digits_to_hash = translator(from=string.digits, to='#')While the latter may appear to be a bit of a special case, it is a
>>> digits_to_hash('Chris Perkins : 224-7992')
'Chris Perkins : ###-####'
task that keeps coming up for me every once in a while.I had to make one arbitrary design decision in this
recipenamely, I decided that the delete
parameter "trumps" the
keep parameter if they overlap:
>>> trans = translator(delete='abcd', keep='cdef')For your applications it might be preferable to ignore
>>> trans('abcdefg')
'ef'
delete if keep is specified, or,
perhaps better, to raise an exception if they are both specified,
since it may not make much sense to let them both be given in the
same call to translator, anyway. Also: as noted in
Recipe 1.8 and Recipe 1.10, the code in this
recipe works only for normal strings, not for
Unicode strings. See Recipe 1.10 to learn how to code this
kind of functionality for Unicode strings, whose
translate method is different from that of plain
(i.e., byte) strings.
See Also
Recipe 1.10 for a direct
equivalent of this recipe's
TRanslator(keep=...), more information on the
TRanslate method, and an equivalent approach for
Unicode strings; documentation for strings'
translate method, and for the
maketrans function in the
string module, in the Library
Reference and Python in a
Nutshell.