文章来自《Python cookbook》.


-- 0.706 [2004-09-29 18:39:31]

Evaluating Code Inside Strings 字符串内代码的求值

Credit: Joonas Paalasmaa

问题 Problem

You have a string that contains embedded Python expressions, and you need to copy the string while evaluating those expressions.


解决 Solution

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:


   1 class Eval:
   2     """mapping that does expression evaluation when asked to fetch an item """
   3     def __getitem__(self, key):
   4         return eval(key)

Now we can perform feats such as:

现在我们能完成表演了, 像是 :

   1 >>> number = 20
   2 >>> text = "python"
   3 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % Eval(  )
   4 Python 2.2 rules!

== 讨论 Discussion ==

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.

在 Python中, % 字符串操作符典型地用作正常的格式化工作。 在字符串中用来替换的值是右边的项目,这或者是tuple,为‘无名值’(unnamed-value)格式,或是一个映射,为‘命名值(named-value)’格式(这里格式项的形式是在 %(name)s)。映射经常从一个函数获得,如内建的 vars ,它返回一个表示局部变量当前状态的字典。

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.

命名值(Named-value)格式实际上要灵活得多。因为对格式中的每个名字字符串格式,它被放在刮弧中,在格式串中跟在指示格式项开始的‘%’字符的后边,Python将调用右边映射的get-item方法.(举例来说,当右边是一个实例对象的时候,调用特殊方法 _ _getitem_ _ )。该方法能完成必需的计算。本配方只是展现了这种可能性,简单地把项目获取委派给内建的函数 eval,把名字作为表达式求值。这在实践中可能是非常有用的,但是如解决方案所呈现,它被限制为只存取定义了Eval类的模块的全局变量。那使它很难适合于大多数实践。

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:

当然这个问题容易被修正,因为 sys._getframe 函数(在Python2.1中和稍后)使得认识到调用者的局部和全局的变量成为很容易的事情。因此,你能定制求值环境:

   1 import sys
   2 class Evalx:
   3     def _ _init_ _(self, locals=None, globals=None):
   4         if locals is None: self.locals = sys._getframe(1).f_locals
   5         else: self.locals = locals
   6         if globals is None: self.globals = sys._getframe(1).f_globals
   7         else: self.globals = globals
   8     def _ _getitem_ _(self, name):
   9         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.


参考 See Also

Recipe 3.22, Recipe 3.23, and Recipe 14.9.