Python 将表达式解析为列表
我得到一个使用偏执和+,例如((a+b)+c)+(d+e)的表达式 我需要找到这个解析树,然后打印这个解析树的列表,如下所示: [a,b,c],[d,e]] 我想我应该使用类似ast的东西,然后是ast2list。然而,由于我没有完全理解这些,我反复出现语法错误。这就是我所拥有的:Python 将表达式解析为列表,python,parsing,expression,Python,Parsing,Expression,我得到一个使用偏执和+,例如((a+b)+c)+(d+e)的表达式 我需要找到这个解析树,然后打印这个解析树的列表,如下所示: [a,b,c],[d,e]] 我想我应该使用类似ast的东西,然后是ast2list。然而,由于我没有完全理解这些,我反复出现语法错误。这就是我所拥有的: import ast import parser a = ast.parse("(((a+b)+c)+(d+e))", mode='eval') b = parser.ast2list(a) print(b)
import ast
import parser
a = ast.parse("(((a+b)+c)+(d+e))", mode='eval')
b = parser.ast2list(a)
print(b)
有人能给我指引正确的方向吗?谢谢。科琳的评论可以通过以下方式实现:
str = "(((a+b)+c)+(d+e))"
replacements = [
('(','['),
(')',']'),
('+',','),
# If a,b,c,d,e are defined variables, you don't need the following 5 lines
('a',"'a'"),
('b',"'b'"),
('c',"'c'"),
('d',"'d'"),
('e',"'e'"),
]
for (f,s) in replacements:
str = str.replace(f,s)
obj = eval(str)
print(str) # [[['a','b'],'c'],['d','e']]
print(obj) # [[['a', 'b'], 'c'], ['d', 'e']]
# You can access the parsed elements as you would any iterable:
print(obj[0]) # [['a', 'b'], 'c']
print(obj[1]) # ['d', 'e']
print(obj[1][0]) # d
科琳的评论可以通过以下方式实现:
str = "(((a+b)+c)+(d+e))"
replacements = [
('(','['),
(')',']'),
('+',','),
# If a,b,c,d,e are defined variables, you don't need the following 5 lines
('a',"'a'"),
('b',"'b'"),
('c',"'c'"),
('d',"'d'"),
('e',"'e'"),
]
for (f,s) in replacements:
str = str.replace(f,s)
obj = eval(str)
print(str) # [[['a','b'],'c'],['d','e']]
print(obj) # [[['a', 'b'], 'c'], ['d', 'e']]
# You can access the parsed elements as you would any iterable:
print(obj[0]) # [['a', 'b'], 'c']
print(obj[1]) # ['d', 'e']
print(obj[1][0]) # d
我也会成为一名翻译。为此,通过ast实现它有点麻烦 应该给
[[a,b],c,[d,[e,f]]]
注意-我的收藏中一直有这个片段。它来自Python食谱。它对字符串进行多次替换,在一次传递中使用字典中的替换键和值。我也会制作一个翻译程序。为此,通过ast实现它有点麻烦 应该给
[[a,b],c,[d,[e,f]]]
注意-我的收藏中一直有这个片段。它来自Python食谱。它使用字典中的替换键和值一次性对字符串进行多次替换。查看ast模块的文档,其中描述了
NodeVisitor
类
import ast
import sys
class MyNodeVisitor(ast.NodeVisitor):
op_dict = {
ast.Add : '+',
ast.Sub : '-',
ast.Mult : '*',
}
type_dict = {
ast.BinOp: lambda s, n: s.handleBinOp(n),
ast.Name: lambda s, n: getattr(n, 'id'),
ast.Num: lambda s, n: getattr(n, 'n'),
}
def __init__(self, *args, **kwargs):
ast.NodeVisitor.__init__(self, *args, **kwargs)
self.ast = []
def handleBinOp(self, node):
return (self.op_dict[type(node.op)], self.handleNode(node.left),
self.handleNode(node.right))
def handleNode(self, node):
value = self.type_dict.get(type(node), None)
return value(self, node)
def visit_BinOp(self, node):
op = self.handleBinOp(node)
self.ast.append(op)
def visit_Name(self, node):
self.ast.append(node.id)
def visit_Num(self, node):
self.ast.append(node.n)
def currentTree(self):
return reversed(self.ast)
a = ast.parse(sys.argv[1])
visitor = MyNodeVisitor()
visitor.visit(a)
print list(visitor.currentTree())
看起来像这样:
$ ./ast_tree.py "5 + (1 + 2) * 3"
[('+', 5, ('*', ('+', 1, 2), 3))]
享受。查看ast模块的文档,其中描述了
NodeVisitor
类
import ast
import sys
class MyNodeVisitor(ast.NodeVisitor):
op_dict = {
ast.Add : '+',
ast.Sub : '-',
ast.Mult : '*',
}
type_dict = {
ast.BinOp: lambda s, n: s.handleBinOp(n),
ast.Name: lambda s, n: getattr(n, 'id'),
ast.Num: lambda s, n: getattr(n, 'n'),
}
def __init__(self, *args, **kwargs):
ast.NodeVisitor.__init__(self, *args, **kwargs)
self.ast = []
def handleBinOp(self, node):
return (self.op_dict[type(node.op)], self.handleNode(node.left),
self.handleNode(node.right))
def handleNode(self, node):
value = self.type_dict.get(type(node), None)
return value(self, node)
def visit_BinOp(self, node):
op = self.handleBinOp(node)
self.ast.append(op)
def visit_Name(self, node):
self.ast.append(node.id)
def visit_Num(self, node):
self.ast.append(node.n)
def currentTree(self):
return reversed(self.ast)
a = ast.parse(sys.argv[1])
visitor = MyNodeVisitor()
visitor.visit(a)
print list(visitor.currentTree())
看起来像这样:
$ ./ast_tree.py "5 + (1 + 2) * 3"
[('+', 5, ('*', ('+', 1, 2), 3))]
享受。这是一个非常简单的问题,您可以从头开始编写解决方案。这假设所有变量名都是一个字符长,或者表达式已正确转换为标记列表。我加入了检查,以确保所有括号都匹配;显然,您应该将
CustomError
替换为您想要抛出的任何异常或您想要执行的其他操作
def expr_to_list(ex):
tree = []
stack = [tree]
for c in ex:
if c == '(':
new_node = []
stack[-1].append(new_node)
stack.append(new_node)
elif c == '+' or c == ' ':
continue
elif c == ')':
if stack[-1] == tree:
raise CustomError('Unmatched Parenthesis')
stack.pop()
else:
stack[-1].append(c)
if stack[-1] != tree:
raise CustomError('Unmatched Parenthesis')
return tree
测试:
>>> expr_to_list('a + (b + c + (x + (y + z) + (d + e)))')
['a', ['b', 'c', ['x', ['y', 'z'], ['d', 'e']]]]
对于多字符变量名,使用正则表达式进行标记化:
>>> tokens = re.findall('\(|\)|\+|[\w]+',
'(apple + orange + (banana + grapefruit))')
>>> tokens
['(', 'apple', '+', 'orange', '+', '(', 'banana', '+', 'grapefruit', ')', ')']
>>> expr_to_list(tokens)
[['apple', 'orange', ['banana', 'grapefruit']]]
这是一个非常简单的问题,您可以从头开始编写解决方案。这假设所有变量名都是一个字符长,或者表达式已正确转换为标记列表。我加入了检查,以确保所有括号都匹配;显然,您应该将
CustomError
替换为您想要抛出的任何异常或您想要执行的其他操作
def expr_to_list(ex):
tree = []
stack = [tree]
for c in ex:
if c == '(':
new_node = []
stack[-1].append(new_node)
stack.append(new_node)
elif c == '+' or c == ' ':
continue
elif c == ')':
if stack[-1] == tree:
raise CustomError('Unmatched Parenthesis')
stack.pop()
else:
stack[-1].append(c)
if stack[-1] != tree:
raise CustomError('Unmatched Parenthesis')
return tree
测试:
>>> expr_to_list('a + (b + c + (x + (y + z) + (d + e)))')
['a', ['b', 'c', ['x', ['y', 'z'], ['d', 'e']]]]
对于多字符变量名,使用正则表达式进行标记化:
>>> tokens = re.findall('\(|\)|\+|[\w]+',
'(apple + orange + (banana + grapefruit))')
>>> tokens
['(', 'apple', '+', 'orange', '+', '(', 'banana', '+', 'grapefruit', ')', ')']
>>> expr_to_list(tokens)
[['apple', 'orange', ['banana', 'grapefruit']]]
如果你真的想做一个解析器,首先不要写任何代码,而是要理解你的语法应该如何工作。或者BNF是用来定义语法的典型符号。是一个常见的软件工程解析主题,中缀符号的基本BNF结构如下所示:
letter ::= 'a'..'z'
operand ::= letter+
term ::= operand | '(' expr ')'
expr ::= term ( '+' term )*
关键是term
包含字母操作数或用()包装的整个子表达式。该子表达式与整个表达式相同,因此此递归定义负责所有括号嵌套。然后,表达式是一个后跟零个或多个术语的术语,使用二进制“+”运算符添加。(您可以扩展术语
来处理减法和乘法/除法,但我不会让这个答案变得过于复杂。)
Pyparsing是一个软件包,它使得使用Python对象(Ply、spark和YAPP是其他解析器,它们遵循更传统的解析器创建lex/yacc模型)将BNF转换为工作解析器变得非常容易。下面是直接使用pyparsing实现的BNF:
from pyparsing import Suppress, Word, alphas, Forward, Group, ZeroOrMore
LPAR, RPAR, PLUS = map(Suppress, "()+")
operand = Word(alphas)
# forward declare our overall expression, necessary when defining a recursive grammar
expr = Forward()
# each term is either an alpha operand, or an expr in ()'s
term = operand | Group(LPAR + expr + RPAR)
# define expr as a term, with optional '+ term's
expr << term + ZeroOrMore(PLUS + term)
# try it out
s = "(((a+b)+c)+(d+e))"
print expr.parseString(s)
识别操作优先级的中缀符号是一种非常常见的解析器,或者是更大的解析器的一部分,因此pyparsing包含一个helper内置调用OperatorRecessence
,以处理所有嵌套/分组/递归等。下面是使用OperatorRecessence
编写的同一个解析器:
from pyparsing import operatorPrecedence, opAssoc, Word, alphas, Suppress
# define an infix notation with precedence of operations
# you only define one operation '+', so this is a simple case
operand = Word(alphas)
expr = operatorPrecedence(operand,
[
('+', 2, opAssoc.LEFT),
])
print expr.parseString(s)
给出与以前相同的结果
更详细的示例可以在pyparsing wiki上在线找到,pyparsing wiki是位于的显式实现,OperatorRecessence实现位于。如果你真的想做一个解析器,首先不要编写任何代码,而是要理解语法应该如何工作。或者BNF是用来定义语法的典型符号。是一个常见的软件工程解析主题,中缀符号的基本BNF结构如下所示:
letter ::= 'a'..'z'
operand ::= letter+
term ::= operand | '(' expr ')'
expr ::= term ( '+' term )*
关键是term
包含字母操作数或用()包装的整个子表达式。该子表达式与整个表达式相同,因此此递归定义负责所有括号嵌套。然后,表达式是一个后跟零个或多个术语的术语,使用二进制“+”运算符添加。(您可以扩展术语
来处理减法和乘法/除法,但我不会让这个答案变得过于复杂。)
Pyparsing是一个软件包,它使得使用Python对象(Ply、spark和YAPP是其他解析器,它们遵循更传统的解析器创建lex/yacc模型)将BNF转换为工作解析器变得非常容易。下面是直接使用pyparsing实现的BNF:
from pyparsing import Suppress, Word, alphas, Forward, Group, ZeroOrMore
LPAR, RPAR, PLUS = map(Suppress, "()+")
operand = Word(alphas)
# forward declare our overall expression, necessary when defining a recursive grammar
expr = Forward()
# each term is either an alpha operand, or an expr in ()'s
term = operand | Group(LPAR + expr + RPAR)
# define expr as a term, with optional '+ term's
expr << term + ZeroOrMore(PLUS + term)
# try it out
s = "(((a+b)+c)+(d+e))"
print expr.parseString(s)
识别操作优先级的中缀符号是一种非常常见的解析器,或者是更大的解析器的一部分,因此pyparsing包含一个helper内置调用OperatorRecessence
,以处理所有嵌套/分组/递归等。下面是使用OperatorRecessence
编写的同一个解析器:
from pyparsing import operatorPrecedence, opAssoc, Word, alphas, Suppress
# define an infix notation with precedence of operations
# you only define one operation '+', so this is a simple case
operand = Word(alphas)
expr = operatorPrecedence(operand,
[
('+', 2, opAssoc.LEFT),
])
print expr.parseString(s)
给出与以前相同的结果
更详细的例子可以在pyparsing wiki上在线找到——在的显式实现和在的OperatorRecessence实现。如果它只是括号和+,为什么不做字符串替换,然后将字符串作为列表进行计算呢?只是出于好奇,如果表达式是
((a+b)+c)+(d+e+f))你会得到什么
?我