在 Python 中处理嵌套布尔查询字符串
Manipulate Nested Boolean Query String in Python
我有这样的字符串布尔查询
queryString= """And(
OR(abc,xyz,wxy),
AND(AND(xyz,wxy),xzy),
XOR(x1,y1, AND(xy,zz))
)"""
目前我很难修改上面的查询字符串,因为我想
- 在最后一个
XOR
中再添加一个OR(x3,y3)
- 删除整个
OR(abc,xyz,wxy)
具有所需的输出
resultQueryString= """And(
AND(AND(xyz,wxy),xzy),
XOR(x1,y1, AND(xy,zz),OR(x3,y3))
)"""
我想我不能轻易做到,除非我为每个不同的查询想出一个复杂的正则表达式。
我正在尝试编写一个 python 函数,它将上述字符串布尔查询作为输入并输出树数据结构。
这样我就可以遍历树并评估或更改我想更改的查询的任何部分。
在上面的例子中,如果我把它当作一棵树,我可以很容易地看到根是 AND
和 traverse/modify 其他分支等等。
ast.parse
函数似乎几乎完全符合您的要求:
ast.dump(ast.parse("""And(
AND(AND(xyz,wxy),xzy),
XOR(x1,y1, AND(xy,zz),OR(x3,y3))
)""").body[0].value)
Call(func=Name(id='And', ctx=Load()), args=[Call(func=Name(id='AND', ctx=Load()), args=[Call(func=Name(id='AND', ctx=Load()), args=[Name(id='xyz', ctx=Load()), Name(id='wxy', ctx=Load())], keywords=[], starargs=None, kwargs=None), Name(id='xzy', ctx=Load())], keywords=[], starargs=None, kwargs=None), Call(func=Name(id='XOR', ctx=Load()), args=[Name(id='x1', ctx=Load()), Name(id='y1', ctx=Load()), Call(func=Name(id='AND', ctx=Load()), args=[Name(id='xy', ctx=Load()), Name(id='zz', ctx=Load())], keywords=[], starargs=None, kwargs=None), Call(func=Name(id='OR', ctx=Load()), args=[Name(id='x3', ctx=Load()), Name(id='y3', ctx=Load())], keywords=[], starargs=None, kwargs=None)], keywords=[], starargs=None, kwargs=None)], keywords=[], starargs=None, kwargs=None)
(.body[0].value
去掉了两个无意义的抽象层,.dump
只是为了输出。
下面是对输出执行您请求的转换的代码:
class Filterer(ast.NodeTransformer):
def visit_Call(self, node):
name=node.func.id
if name == "OR" and len(node.args) == 3:
return None
elif name == "XOR":
args = [ast.Name("x3",ast.Load()),
ast.Name("y3",ast.Load())]
func = ast.Name("OR",ast.Load())
node.args.append(ast.Call(func, args, [], None, None))
return self.generic_visit(node)
这是以您的格式打印结果的代码,但空格除外:(Python 在其 ast
模块中没有为此内置的):
class Printer(ast.NodeVisitor):
def visit_Call(self, node):
self.visit(node.func)
print("(",end="")
comma = False
for arg in node.args:
if comma:
print(",",end="")
comma=True
self.visit(arg)
print(")",end="")
def visit_Name(self, node):
print(node.id,end="")
因此,最终代码为:
Printer().visit(Filterer().visit(ast.parse(queryString)))
我有这样的字符串布尔查询
queryString= """And(
OR(abc,xyz,wxy),
AND(AND(xyz,wxy),xzy),
XOR(x1,y1, AND(xy,zz))
)"""
目前我很难修改上面的查询字符串,因为我想
- 在最后一个
XOR
中再添加一个OR(x3,y3)
- 删除整个
OR(abc,xyz,wxy)
具有所需的输出
resultQueryString= """And(
AND(AND(xyz,wxy),xzy),
XOR(x1,y1, AND(xy,zz),OR(x3,y3))
)"""
我想我不能轻易做到,除非我为每个不同的查询想出一个复杂的正则表达式。
我正在尝试编写一个 python 函数,它将上述字符串布尔查询作为输入并输出树数据结构。
这样我就可以遍历树并评估或更改我想更改的查询的任何部分。
在上面的例子中,如果我把它当作一棵树,我可以很容易地看到根是 AND
和 traverse/modify 其他分支等等。
ast.parse
函数似乎几乎完全符合您的要求:
ast.dump(ast.parse("""And(
AND(AND(xyz,wxy),xzy),
XOR(x1,y1, AND(xy,zz),OR(x3,y3))
)""").body[0].value)
Call(func=Name(id='And', ctx=Load()), args=[Call(func=Name(id='AND', ctx=Load()), args=[Call(func=Name(id='AND', ctx=Load()), args=[Name(id='xyz', ctx=Load()), Name(id='wxy', ctx=Load())], keywords=[], starargs=None, kwargs=None), Name(id='xzy', ctx=Load())], keywords=[], starargs=None, kwargs=None), Call(func=Name(id='XOR', ctx=Load()), args=[Name(id='x1', ctx=Load()), Name(id='y1', ctx=Load()), Call(func=Name(id='AND', ctx=Load()), args=[Name(id='xy', ctx=Load()), Name(id='zz', ctx=Load())], keywords=[], starargs=None, kwargs=None), Call(func=Name(id='OR', ctx=Load()), args=[Name(id='x3', ctx=Load()), Name(id='y3', ctx=Load())], keywords=[], starargs=None, kwargs=None)], keywords=[], starargs=None, kwargs=None)], keywords=[], starargs=None, kwargs=None)
(.body[0].value
去掉了两个无意义的抽象层,.dump
只是为了输出。
下面是对输出执行您请求的转换的代码:
class Filterer(ast.NodeTransformer):
def visit_Call(self, node):
name=node.func.id
if name == "OR" and len(node.args) == 3:
return None
elif name == "XOR":
args = [ast.Name("x3",ast.Load()),
ast.Name("y3",ast.Load())]
func = ast.Name("OR",ast.Load())
node.args.append(ast.Call(func, args, [], None, None))
return self.generic_visit(node)
这是以您的格式打印结果的代码,但空格除外:(Python 在其 ast
模块中没有为此内置的):
class Printer(ast.NodeVisitor):
def visit_Call(self, node):
self.visit(node.func)
print("(",end="")
comma = False
for arg in node.args:
if comma:
print(",",end="")
comma=True
self.visit(arg)
print(")",end="")
def visit_Name(self, node):
print(node.id,end="")
因此,最终代码为:
Printer().visit(Filterer().visit(ast.parse(queryString)))