gpt4 book ai didi

python - 在 Python 中为表达式定义新的语义

转载 作者:太空狗 更新时间:2023-10-30 00:14:29 25 4
gpt4 key购买 nike

我想定义基于 Python 的约束规范语言。例如:

x = IntVar()
c = Constraint(x < 19)
c.solve()

在这里IntVar是一个描述变量的类,该变量可以采用任何整数值,并且 Constraint是表示约束的类。为了实现这个,我可以重载运算符 <通过定义方法 __lt__上课 IntVar .

现在假设我要声明 10 < x < 19 .我想写这样的东西:

c = Constraint(x > 10 and x < 19)

不幸的是,我不能这样做,因为and不能在 Python 中重载。使用 &而不是 and不是一个选项,因为它的优先级和按位 &在约束语言中有其正确的含义,例如 (x & 0x4) == 1 .

您有什么建议?

作为变通方法,我使用带引号的表达式来约束:

c = Constraint("x < 19")

但这需要实现我宁愿避免的约束语言解析,而且更重要的是,只有在实际完成解析时才能检查句法正确性。因此,用户可能要花几个小时才能发现约束定义中存在语法错误。

我考虑的另一个选择是使用一种 lambda 表达式来定义约束:

c = Constraint(lambda: x < 19)

但我无法访问 lambda 对象的解析树。

最佳答案

使用 & , |~其实是一个很不错的选择。您只需记录由于不同的运算符优先级而需要括号。

例如,SQLAlchemy 就是这样做的。对于不喜欢这种滥用位运算符的人,它还提供了and_(*args)。 , or_(*args) , 和 not_(arg)功能与他们的运营商同行做同样的事情。但是,您被迫添加前缀符号 ( and_(foo, bar) ),这不如中缀符号 ( foo & bar ) 可读。


lambda方法也是一个好主意(除了 lambda 本身引入的丑陋之外)。不幸的是,如果没有源代码,AST 确实不可用 - 但是等等,您确实有源代码,只是没有附加到函数对象!

想象一下这段代码:

import ast
import inspect

def evaluate(constraint):
print ast.dump(ast.parse(inspect.getsource(constraint)))

evaluate(lambda x: x < 5 and x > -5)

这会给你这个 AST:

Module(
body=[
Expr(
value=Call(
func=Name(id='evaluate', ctx=Load()), args=[
Lambda(
args=arguments(
args=[
Name(id='x', ctx=Param())
],
vararg=None,
kwarg=None,
defaults=[]
),
body=BoolOp(
op=And(),
values=[
Compare(
left=Name(id='x', ctx=Load()),
ops=[Lt()],
comparators=[Num(n=5)]
),
Compare(
left=Name(id='x', ctx=Load()),
ops=[Gt()],
comparators=[Num(n=-5)]
)
]
)
)
],
keywords=[],
starargs=None,
kwargs=None
)
)
]
)

缺点是您获得了整个源代码行 - 但您可以轻松地遍历 AST 直到到达您的 lambda 表达式(对评估函数的调用中的第一个表达式),然后您可以只处理相关部分。

为了避免自己评估它,您现在可以简单地重写 AST 以使用按位运算符,然后将新的 AST 编译为一个函数,该函数将使用可重载运算符。

让我们看一下 ((x < 5) & (x > -5)) 的 AST :

body=BinOp(
left=Compare(
left=Name(id='x', ctx=Load()),
ops=[Lt()],
comparators=[Num(n=5)]
),
op=BitAnd(),
right=Compare(
left=Name(id='x', ctx=Load()),
ops=[Gt()],
comparators=[Num(n=-5)]
)
)

如您所见,差异非常小。您只需重写 AST 的 BoolOp 即可使用 BinOp!

and_(x < 5, x > -5) 的 AST看起来像这样:

body=Call(
func=Name(id='and_', ctx=Load()),
args=[
Compare(
left=Name(id='x', ctx=Load()),
ops=[Lt()],
comparators=[Num(n=5)]
),
Compare(
left=Name(id='x', ctx=Load()),
ops=[Gt()],
comparators=[Num(n=-5)]
)
],
keywords=[],
starargs=None,
kwargs=None
)

重写也不难。

关于python - 在 Python 中为表达式定义新的语义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25469423/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com