我正在尝试在表达式中记录分组,例如:
"{
"title" : "risk",
"name" : "David"
}"
这些是用户可以编写以配置SQL查询的表达式,因此上述内容最终将表示为"{
"title" : "risk",
"name" : "John",
"city" : "paris"
}"
。
我试过了:
condition_a = Condition( "x = 5 ")
condition_b = Condition( "y > 6 " )
condition_c = Condition( "z = 7 " )
condition_c & ( condition_a | condition_b )
我希望这段代码:
x = 5 AND (y > 6 OR z = 7)
打印class Condition( object ) :
def __init__( self, expr ) :
....
def __and__( self , other ) :
print 'calling and'
....
return self
def __or__( self , other ) :
print 'calling or'
....
return self
def __call__( self , other ) :
print 'calling ()'
....
return self
但不是。我只是看到了:
condition_a = Condition( "x = 5 ")
condition_b = Condition( "y > 6 " )
condition_c = Condition( "z = 7 " )
condition = condition_c & ( condition_a | condition_b )
expected = "z = 7 AND ( x = 5 OR y > 6 )"
似乎我没有使用通话,因此类似calling ()
,因此calling or
calling and
不会被调用。
有没有办法实现我想要的?您可以检查我想要的预期内容(和和或的工作方式,我只是在表达式的一部分没有括号)。
答案 0 :(得分:3)
不,表达式中的(...)
括号不是调用表达式。他们只是将你的表达分组。
Python不保留分组括号。它们仅由解析器用于构建抽象语法树,并将影响分组为执行的表达式。
表达式
A & B | C
被解析为BinOp
个对象的树,如下所示:
|
/ \
& C
/ \
A B
&
在这里绑定得更紧,所以A & B
在|
运算符执行之前执行。那是因为|
operator has a lower precedence than &
。
括号告诉解析器在将(...)
放入树之前为A & (B | C)
之间的所有内容构建单独的树,因此表达式
&
/ \
A |
/ \
B C
变为
|
括号已经消失,剩下的就是表达式树,Python知道你需要以什么顺序执行这些表达式;首先在B
和C
执行&
,然后在>>> import ast
>>> print(ast.dump(ast.parse('A & B | C').body[0].value)))
BinOp(
left=BinOp(
left=Name(id='A', ctx=Load()),
op=BitAnd(),
right=Name(id='B', ctx=Load())
),
op=BitOr(),
right=Name(id='C', ctx=Load()))
)
>>> print(ast.dump(ast.parse('A & (B | C)').body[0].value))
BinOp(
left=Name(id='A', ctx=Load()),
op=BitAnd(),
right=BinOp(
left=Name(id='B', ctx=Load()),
op=BitOr(),
right=Name(id='C', ctx=Load())
)
)
运算符中使用此表达式的结果。
您可以使用ast
module:
Module
(我解开了外部Expr
和BinOp
对象以关注>>> dis.dis(compile('A & B | C', '', 'eval'))
1 0 LOAD_NAME 0 (A)
2 LOAD_NAME 1 (B)
4 BINARY_AND
6 LOAD_NAME 2 (C)
8 BINARY_OR
10 RETURN_VALUE
>>> dis.dis(compile('A & (B | C)', '', 'eval'))
1 0 LOAD_NAME 0 (A)
2 LOAD_NAME 1 (B)
4 LOAD_NAME 2 (C)
6 BINARY_OR
8 BINARY_AND
10 RETURN_VALUE
树,并手动缩进嵌套对象。
当您使用dis
module查看生成的字节码时,您可以看到操作顺序发生变化:
LOAD_NAME
(BINARY_OR
在堆栈上放置一个值,BINARY_AND
和class Expr(object):
def __or__(self, other):
return BinOp(self, '|', other)
def __and__(self, other):
return BinOp(self, '&', other)
class BinOp(Expr):
def __init__(self, left, oper, right):
self.left, self.oper, self.right = left, oper, right
def __repr__(self):
return "({} {} {})".format(self.left, self.oper, self.right)
class Condition(Expr):
def __init__(self, expr):
self.expr = expr
def __repr__(self):
return self.expr
从堆栈中取出前2个值并推回结果。)
所有这些意味着无法以这种方式检索括号。他们走了,他们只是在那里通知操作的顺序。
您需要做的是维护语法树;你仍然可以从执行中重建它。然后,当您输出结果时,总是包括括号:
BinOp
打印>>> condition_a = Condition( "x = 5 ")
>>> condition_b = Condition( "y > 6 " )
>>> condition_c = Condition( "z = 7 " )
>>> condition_c & ( condition_a | condition_b )
(z = 7 | (x = 5 | y > 6 ))
>>> condition_c & condition_a | condition_b
((z = 7 | x = 5 ) | y > 6 )
对象时,总是得到括号:
# Condition stays the same
class Expr(object):
precedence = float('inf')
def __or__(self, other):
return BinOp(self, '|', other)
def __and__(self, other):
return BinOp(self, '&', other)
class BinOp(Expr):
def __init__(self, left, oper, right):
self.left, self.oper, self.right = left, oper, right
self.precedence = '|&'.index(oper) # 0 or 1
def __repr__(self):
left, right = self.left, self.right
if left.precedence < self.precedence:
left = '({})'.format(left)
if right.precedence <= self.precedence:
right = '({})'.format(right)
return "{} {} {}".format(left, self.oper, right)
这是好,因为SQL解析器以相同的方式处理括号;只是为了分组表达。
下一步是将优先级信息添加到二元运算符。然后,您可以决定是否必须在子表达式周围加上括号;只有当左子表达式具有较低的优先级,或者右边的优先级较低或相等时,才需要它们:
>>> condition_c & ( condition_a | condition_b )
z = 7 & (x = 5 | y > 6 )
>>> condition_c & condition_a | condition_b
z = 7 & x = 5 | y > 6
>>> condition_c & (condition_a & condition_b)
z = 7 & (x = 5 & y > 6 )
现在括号只在表达式需要时出现:
0
如果要扩展此系统(添加更多运算符类型),则必须扩展优先级的概念;上面的例子对于二元运算符只有1
和{{1}},但是如果你有更多的话,你必须自然地扩展这些值。
我也强烈敦促你看看SQLAlchemy,它已经实现了所有这些,等等。