使用eval()
可能不是一个好主意,但如果你坚持(Python 3):
call = lambda f: lambda *args: f(*args)
flip = lambda f: lambda *args: f(*reversed(args))
class Expression:
def __repr__(self):
return '{}({})'.format(type(self).__name__, self)
class BinaryExpression(Expression):
def __init__(self, left, right):
self.left = promote(left)
self.right = promote(right)
def __str__(self):
return '({} {} {})'.format(self.op, self.left, self.right)
class Variable(Expression):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
class Number(Expression):
def __init__(self, value):
self.value = int(value)
def __str__(self):
return str(self.value)
class Multiplication(BinaryExpression):
op = '*'
class Addition(BinaryExpression):
op = '+'
class Smaller(BinaryExpression):
op = '<'
class Greater(BinaryExpression):
op = '>'
class SmallerOrEqual(BinaryExpression):
op = '<='
class GreaterOrEqual(BinaryExpression):
op = '>='
Expression.__mul__ = call(Multiplication)
Expression.__rmul__ = flip(Multiplication)
Expression.__add__ = call(Addition)
Expression.__radd__ = flip(Addition)
Expression.__lt__ = call(Smaller)
Expression.__gt__ = call(Greater)
Expression.__le__ = call(SmallerOrEqual)
Expression.__ge__ = call(GreaterOrEqual)
def promote(item):
if isinstance(item, str):
return Variable(item)
elif isinstance(item, int):
return Number(item)
else:
return item
class LpVariable:
def __init__(self, name, x, y, z):
self.name = name
self.x = x
self.y = y
self.z = z
def __str__(self):
return 'LpVariable({}, {}, {}, {})'.format(
self.name,
self.x,
self.y,
self.z,
)
__repr__ = __str__
LpContinuous = 'LpContinuous'
class ExpressionVisitor:
def visit(self, node):
return getattr(self, 'visit_' + type(node).__name__)(node)
class LpTransformer(ExpressionVisitor):
def visit_Variable(self, node):
return LpVariable(node.name, None, None, LpContinuous)
def visit_Number(self, node):
return node.value
def visit_Multiplication(self, node):
return [node.op, self.visit(node.left), self.visit(node.right)]
def visit_Addition(self, node):
return [node.op, self.visit(node.left), self.visit(node.right)]
def visit_Smaller(self, node):
return [node.op, self.visit(node.left), self.visit(node.right)]
def visit_Greater(self, node):
return [node.op, self.visit(node.left), self.visit(node.right)]
def visit_SmallerOrEqual(self, node):
return [node.op, self.visit(node.left), self.visit(node.right)]
def visit_GreaterOrEqual(self, node):
return [node.op, self.visit(node.left), self.visit(node.right)]
class Evaluator(ExpressionVisitor):
def __init__(self, **env):
self.env = env
def visit_Variable(self, node):
return self.env[node.name]
def visit_Number(self, node):
return node.value
def visit_Multiplication(self, node):
return self.visit(node.left) * self.visit(node.right)
def visit_Addition(self, node):
return self.visit(node.left) + self.visit(node.right)
def visit_Smaller(self, node):
return self.visit(node.left) < self.visit(node.right)
def visit_Greater(self, node):
return self.visit(node.left) > self.visit(node.right)
def visit_SmallerOrEqual(self, node):
return self.visit(node.left) <= self.visit(node.right)
def visit_GreaterOrEqual(self, node):
return self.visit(node.left) >= self.visit(node.right)
class Namespace(dict):
def __missing__(self, key):
value = self[key] = Variable(key)
return value
def main():
constraints = '2*a + 3*b <= c'
namespace = Namespace()
tree = eval(constraints, {}, namespace)
print('AST in prefix notation:', tree)
print()
print('Namespace:', namespace)
print()
print('LP-Transformed tree:')
import pprint
pprint.pprint(LpTransformer().visit(tree))
print()
print('Evaluated with a=3, b=5, c=10:')
pprint.pprint(Evaluator(a=3, b=5, c=10).visit(tree))
print()
print('Evaluated with a=3, b=5, c=100:')
pprint.pprint(Evaluator(a=3, b=5, c=100).visit(tree))
if __name__ == '__main__':
main()
结果:
AST in prefix notation: (<= (+ (* 2 a) (* 3 b)) c)
Namespace: {'a': Variable(a), 'c': Variable(c), 'b': Variable(b)}
LP-Transformed tree:
['<=',
['+',
['*', 2, LpVariable(a, None, None, LpContinuous)],
['*', 3, LpVariable(b, None, None, LpContinuous)]],
LpVariable(c, None, None, LpContinuous)]
Evaluated with a=3, b=5, c=10:
False
Evaluated with a=3, b=5, c=100:
True
LpVariable
课程显然是一个模型。此外,该LpTransformer
课程应该产生可供纸浆使用的东西。只需相应地更改visit_*
方法即可。
数字都是int
s,你可能不想要。您可能应该添加float
s 和/或将所有数字转换为decimal.Decimal
.
我可能会编写一个真正的解析器,而不是eval()
使用 pyparsing,或者,我最喜欢这样的东西,Parcon。