2

我正在寻求有关如何“接近”使and运算符超载的建议。提示我在做什么的代码示例:

import operator


OP = { operator.ge: ">=", operator.le: "<=" }


class Expression(object):
    def __init__(self, left, right, op):
        self.left = left
        self.right = right
        self.op = op

    def __le__(self, other):
        return Expression(self, other, operator.le)

    def __ge__(self, other):
        return Expression(self, other, operator.ge)

    def __str__(self):
        return "(%s %s %s)" % (self.left, OP[self.op], self.right)

    def __repr__(self):
        return "<Expression: %s %s %s>" % (self.left, self.op, self.right)


class Variable(Expression):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def __repr__(self):
        return "<Variable: %s>" % self.name


def _and(left, right):
    return (left, right)

print 1 <= Variable("x") <= 3
print _and(1 <= Variable("x"), Variable("x") <= 3)

上面的示例打印:

(x <= 3)
(<Expression: x <built-in function ge> 1>, <Expression: x <built-in function le> 3>)

根据Python 文档,表达式1 <= a <= 2转换为1 <= a and a <= 2,所以我猜测在我的示例中1 <= Variable("x")计算结果为,True因此表达式返回 的值Variable("x") <= 3。我需要一种方法来分别获取两个表达式 ( 1 <= a, 和a <= 2),最好不要两次写出中间表达式(就像我对 _and 函数所做的那样),因为中间表达式可能非常复杂且麻烦两次(我意识到我可以将其保存到变量中并使用它,但这也不太可读)。根据this SE postand运算符不能重载,所以我要问的是是否可以这样做,然后如何,或者我是否必须将比较分成两部分(指定中间表达式两次)。任何正确方向的建议表示赞赏!谢谢你。

可能相关:

编辑:所以我基本上做的是为线性程序写出约束,所以我的代码的相关部分目前看起来像这样:

model.add_constr(a <= b)
model.add_constr(b <= c)
# now i could alter add_constr to support either a tuple or two arguments enabling:
model.add_constr(a <= b, b <= c)
model.add_constr((a <= b, b <= c))
# but most pleasing would be if I could do 
model.add_constr(a <= b <= c)
# that would return for example a two tuple that could be interpreted appropriately by 
# add_constr()
4

2 回答 2

2

看起来您想要延迟评估或静态分析类似 Python 的表达式。您是否考虑过使用抽象语法树?astPython 可以使用标准库模块中的工具解析而不评估表达式。请参阅http://docs.python.org/2/library/ast.htmlhttp://eli.thegreenplace.net/2009/11/28/python-internals-working-with-python-asts/

与其尝试过度加载每个运算符,不如将 Python 表达式表示为数据结构,并且可以用它做任何你想做的事情。您可以对树进行修改然后执行结果,或者将树转换为您自己的ExpressionVariable类。

例如,ast将比较链表示为任意长的表达式和比较运算符列表(请参阅Compare表达式类型)。这就是and-chaining的实现方式。

编辑:这是一个示例用法:

>>> import ast
>>> x = ast.parse("2 + 2")
>>> print ast.dump(x)
Module(body=[Expr(value=BinOp(left=Num(n=2), op=Add(), right=Num(n=2)))])
>>> eval(compile(ast.Interactive(body=[x.body[0]]), "fakefile", "single"))
4

在您的情况下,您可能不会真正让 Python 评估表达式,因为您只想将 Python 用作表达式解析器,而不是解释器。(这就是你的树ExpressionVariable符号树正在有效地做的事情。)你可以将可能的表达式树的一个子集解释为传递给model. 也就是说,您将处理您感兴趣的 Python 子集:符号(变量)、比较运算符、逻辑运算符。其余的可以提出一个SyntaxErrororNotImplementedError或任何您想用来通知您的用户他们正在尝试在已实现的子集之外做某事的东西。

于 2013-07-16T02:30:30.213 回答
0

对于纯粹的句法解决方案,您可以简单地在其中一个比较周围加上括号,以打破 Python 的运算符链:(a <= b) <= c.

不过,我不确定这样做是否有意义。也就是说,如果您使用正常值执行此操作,您通常会得到一个无意义的结果(或者在 Python 3 中可能会出现错误)。它可能会使阅读您的代码的其他人(或您自己,几个月后)感到困惑。

我怀疑更好的解决方案是按照您的考虑去做,并坚持简单的比较当您需要表达双端约束时,请使用两个单端约束。如果中间需要一个复杂的值,请尝试将其保存在变量中,而不是在每个表达式中重新计算:

b_expr = b**4 + 3*b**2 - 4*x - 100
model.add_constr(a <= b_expr, b_expr <= c)
于 2013-07-16T05:30:31.680 回答