Regular string interpolation in Python requires the user to pass an explicit keyword dictionary. This recipe adds a little bif of magic, so that if a name is not found in the passed dictionary, it is looked up in the locals and globals dictionaries. It is also possible not to pass any explicit dictionary, then the names is searched in the locals and globals dictionaries only.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import sys, UserDict
from string import Template
class Chainmap(UserDict.DictMixin):
"""Combine multiple mappings for sequential lookup. Raymond Hettinger,
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305268 """
def __init__(self, *maps):
self._maps = maps
def __getitem__(self, key):
for mapping in self._maps:
try:
return mapping[key]
except KeyError:
pass
raise KeyError(key)
def interp(s, dic = None):
caller = sys._getframe(1)
if dic:
m = Chainmap(dic, caller.f_locals, caller.f_globals)
else:
m = Chainmap(caller.f_locals, caller.f_globals)
return Template(s).substitute(m)
## Example:
language="Python"
def printmsg():
opinion = "favorite"
print interp("My $opinion language is $language.")
|
This is the way I always wanted string interpolation to work.
Adding keywords ? Hi, why not add keywords to the interp() function ?
This way you can write :
Default dic to {}. Then you can rip out the if-then-else
And the cycle completes with the addition of positional args...
Yes yes, I know this is getting a little silly, but I couldn't resist.
please, no dic={}. Using a mutable object as a default argument is a common mistake in Python. Consider this example for instance:
The default argument is shared trough all the function calls.
one-liner. cat = lambda _ : Template(_).substitute(sys._getframe(1).f_locals, **sys._getframe(1).f_globals)
this works just like the old Itpl module, I think. I've used it for a couple of days, only, but it seems quite handy.
x= 'interpolate'
cat("I can $x to my heart's content.")
whoops! need to swap the .f_locals and the .f_globals to keep globals from hiding locals: