Я работаю над кодом Python, чтобы заменить выражение, содержащее «/», фактическим вызовом функции. Например: '(n/7-7) +(n/3+3)' должно стать '(div(n,7)-7 + ( div(n,3)+3)'. Обратите внимание только на '/ ' операнд необходимо заменить.
Для этого я использую ast.NodeVisitor.
class visitor(ast.NodeVisitor):
def visit_Expression(self, node): return self.visit(node.body)
def visit_Name(self, node): return node.id
def visit_Constant(self, node): return str(node.value)
def visit_UnaryOp(self, node): return f'{self.visit(node.op)}({self.visit(node.operand)})'
def visit_UAdd(self, node): return '+'
def visit_USub(self, node): return '-'
def visit_BinOp(self, node):
if isinstance(node.op,ast.Div):
return f'{self.visit(node.op)}({self.visit(node.left)},{self.visit(node.right)})'
else:
return f'{self.visit(node.left)} {self.visit(node.op)} {self.visit(node.right)}'
def visit_Add(self, node): return '+'
def visit_Sub(self, node): return '-'
def visit_Mult(self, node): return '*'
def visit_Div(self, node): return 'div'
def generic_visit(self, node): raise ValueError('Invalid Token')
def tokenize(source):
return visitor().visit(ast.parse(source, mode='eval'))
Я вызываю эту функцию как и получаю вывод div(n,5)-1:
expression = 'n/5-1'
expression = tokenize(expression)
Однако это не работает для тригонометрических функций, таких как tan/радианы. Например:
expression = 'tan(radians(top))'
Нужно ли мне также добавлять visit_tan и visit_radians?
Ты прав. Строки не должны быть возвращены.
Я изменю код, чтобы он возвращал строки для всех операндов, и попробую.






Если вы намерены продолжать использовать свой нынешний подход, я не вижу причин, по которым вам нужно что-то еще, кроме текущего расширения ast.NodeVisitor.
Вы можете просто добавить в свой класс следующий метод:
class visitor(ast.NodeVisitor):
# (...)
def visit_Call(self, node):
return f'{self.visit(node.func)}({','.join(map(self.visit, node.args))})'
tokenize('tan(radians(n/7-7))')
# 'tan(radians(div(n,7) - 7))'
Это позволит прочитать любой вызов функции (например, rad, tan...) и снова записать его как вызов функции в результирующий текст. Если вы хотите ограничить их, чтобы разрешить только определенный список имен функций, вы можете поставить проверку внутри метода и отправлять visit(node.func) только в том случае, если node.func.id есть в вашем списке, в противном случае отправляйте generic_visit, чтобы он мог выдать ошибку.
Похоже, вам нужен NodeTransformer, а не NodeVisitor. Кроме того, этот метод действительно не должен возвращать строки.