Credit: Joonas Paalasmaa
You have a string that contains embedded Python expressions, and you need to copy the string while evaluating those expressions.
This recipe’s trick is to use the
%
string-formatting
operator’s named-values variant. That variant
normally takes a dictionary as the righthand operand, but in fact it
can take any mapping, so we just prepare a rather special mapping for
the recipe’s purpose:
class Eval: """ mapping that does expression evaluation when asked to fetch an item """ def _ _getitem_ _(self, key): return eval(key)
Now we can perform feats such as:
>>> number = 20
>>> text = "python"
>>> print "%(text.capitalize())s %(number/9.0).1f rules!" % Eval( )
Python 2.2 rules!
This recipe can be seen as a templating task, akin to Recipe 3.22 and Recipe 3.23, but it is substantially simpler, because it needs to handle only embedded expressions, not statements. However, because the solution is so much simpler and faster than the general templating ones, it’s better to think of this as a totally separate task.
In Python, the %
operator of strings is typically
used for normal formatting. The values to be interpolated in the
string are the items of the righthand side, which is either a tuple,
for unnamed-value formatting, or a mapping, for named-value
formatting (where format items have forms such as
%(name)s
). The mapping is often obtained by
functions such as the built-in vars
, which returns
a dictionary that represents the current status of local variables.
Named-value formatting is actually
much more flexible. For each name string in the format, which is
enclosed in parentheses after the %
character that
denotes the start of a format item in the format string, Python calls
the get-item method of the righthand-side mapping (e.g., the special
method _ _getitem_ _
, when the righthand side is
an instance object). That method can perform the necessary
computation. The recipe shows off this possibility by simply
delegating item-fetching to the built-in function
eval
, which evaluates the name as an expression.
This can be very useful in practice, but as presented in the
solution, it’s limited to accessing global variables
of the module in which the Eval
class is itself
defined. That makes it unwieldy for most practical purposes.
This problem is easily fixed, of course, because the
sys._getframe
function (in Python 2.1 and later) makes it easy to learn about your
caller’s local and global variables. So, you can
tailor the evaluation environment:
import sys class Evalx: def _ _init_ _(self, locals=None, globals=None): if locals is None: self.locals = sys._getframe(1).f_locals else: self.locals = locals if globals is None: self.globals = sys._getframe(1).f_globals else: self.globals = globals def _ _getitem_ _(self, name): return eval(name, self.globals, self.locals)
See Recipe 14.9 for a way to get the same functionality in other, older versions of Python.
Any instance of the Evalx
class can now be used
for expression evaluation, either with explicitly specified
namespaces or, by default, with the local and global namespaces of
the function that instantiated it.
Recipe 3.22, Recipe 3.23, and Recipe 14.9.