链接比较(重载不可重载的“和”运算符)

时间:2013-07-16 02:06:58

标签: python

我正在询问有关如何“关闭”重载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 docs表达式1 <= a <= 2转换为1 <= a and a <= 2,所以我猜测在我的示例中1 <= Variable("x")评估为True所以表达式返回Variable("x") <= 3的值。我需要一种方法来分别获得两个表达式(1 <= aa <= 2),并且最好不要两次写出中间表达式(就像我对_and函数所做的那样),因为中间表达式可能是相当的有两次复杂和麻烦(我意识到我可以将它保存到变量并使用它,但这也不太可读)。根据{{​​3}},and运算符不能超载,所以我要问的是这是否可以完成,然后是如何,或者我是否必须将比较分成两部分(指定中间表达式两次)。任何正确方向的建议都值得赞赏!谢谢。

可能相关:

编辑: 所以我实际上在做的是为Chained comparisons in SQLAlchemy写出约束,所以我的代码的相关部分目前看起来像这样:

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()

2 个答案:

答案 0 :(得分:2)

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

您可以将Python表达式表示为数据结构,而不是尝试遍历每个运算符,您可以使用它来执行任何操作。您可以对树进行修改然后执行结果,或将树转换为您自己的ExpressionVariable类。

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

修改:以下是一个示例用法:

>>> 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子集:符号(变量),比较运算符,逻辑运算符。其余的可以提出SyntaxErrorNotImplementedError或您想要使用的任何内容,以通知您的用户他们正在尝试在已实现的子集之外执行某些操作。

答案 1 :(得分: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)
相关问题