将该字符串解析为树(或等效可解释的数据结构),只需一次,然后重复使用函数为每个感兴趣的变量分配集“解释树”。(您甚至可以将 Python 字节码生成为“可解释的数据结构”,这样您就可以将eval
其用作“解释树”——这会导致生成缓慢,但只需要一次,并且可以快速解释)。
正如您所说,这有点抽象,所以让我们举一个具体的,如果过于简单化的例子。例如,变量是字母 x、y、z、t,运算符是用于加法的 a 和用于减法的 s —— 相邻字母的字符串隐含地表示高优先级乘法,就像在常见的数学约定中一样;没有括号,并且严格从左到右执行(即没有运算符优先级,除了乘法)。除了这 6 个字符之外的每个字符都必须被忽略。那么,这里是一个非常特殊的解析器和 Python 字节码生成器:
class BadString(Exception): pass
def makeBytecode(thestring):
theoperator = dict(a='+', s='-')
python = []
state = 'start'
for i, letter in enumerate(thestring):
if letter in 'xyzt':
if state == 'start':
python.append(letter)
state = 'variable'
elif state == 'variable':
python.append('*')
python.append(letter)
elif letter in 'as':
if state == 'start':
raise BadString(
'Unexpected operator %r at column %d' % (letter, i))
python.append(theoperator[letter])
state = 'start'
if state != 'variable':
raise BadString(
'Unexpected operator %r at end of string' % letter)
python = ''.join(python)
# sanity check
# print 'Python for %r is %r' % (thestring, python)
return compile(python, thestring, 'eval')
现在,您可以简单地将eval
this 的结果作为第一个参数调用,并将与 x、y、z 和 t 相关联的值作为第二个参数调用。例如(已导入上述模块par
并取消注释完整性检查):
>>> c=par.makeBytecode('xyax')
Python for 'xyax' is 'x*y+x'
>>> for x in range(4):
... for y in range(5):
... print 'x=%s, y=%s: result=%s' % (x,y,eval(c,dict(x=x,y=y)))
...
x=0, y=0: result=0
x=0, y=1: result=0
x=0, y=2: result=0
x=0, y=3: result=0
x=0, y=4: result=0
x=1, y=0: result=1
x=1, y=1: result=2
x=1, y=2: result=3
x=1, y=3: result=4
x=1, y=4: result=5
x=2, y=0: result=2
x=2, y=1: result=4
x=2, y=2: result=6
x=2, y=3: result=8
x=2, y=4: result=10
x=3, y=0: result=3
x=3, y=1: result=6
x=3, y=2: result=9
x=3, y=3: result=12
x=3, y=4: result=15
>>>
对于更复杂但仍然简单的字符串解析和快速可解释数据结构的构建,请参见例如pyparsing。