在python中将ast.Num转换为decimal.decimal以获得精度

在python中将ast.Num转换为decimal.decimal以获得精度,python,floating-point,decimal,abstract-syntax-tree,Python,Floating Point,Decimal,Abstract Syntax Tree,我目前正在编写一个解析器来解析简单的算术公式:它只需要(并限制)在数字和变量上支持+-*/即可。例如: 100.50*num*discount 它基本上用于计算产品的价格 这是用python编写的,为了简单起见,我只想使用python自己的解析器。其思想是首先将输入解析到ast中,然后在ast上行走以将ast的节点类型限制在一个小的子集中,例如:ast.BinOp,ast.Add,ast.Num,ast.Name等等 目前它运行良好,只是ast中的浮点数不精确。因此,我想将ast的ast.Nu

我目前正在编写一个解析器来解析简单的算术公式:它只需要(并限制)在数字和变量上支持+-*/即可。例如:

100.50*num*discount
它基本上用于计算产品的价格

这是用python编写的,为了简单起见,我只想使用python自己的解析器。其思想是首先将输入解析到ast中,然后在ast上行走以将ast的节点类型限制在一个小的子集中,例如:
ast.BinOp
ast.Add
ast.Num
ast.Name
等等

目前它运行良好,只是ast中的浮点数不精确。因此,我想将ast的
ast.Num
节点转换为一些
ast.Call(func=ast.Name(id='Decimal'),…)
。但问题是:
ast.Num
只包含一个
n
字段,该字段是已解析的浮点数。在源代码中获取原始数字文字并不容易:


有什么建议吗?

我建议采用两步方法:在第一步中,使用Python的
标记化
模块将源代码中的所有浮点数字文本转换为格式为
'Decimal(my_numeric_literal)
的字符串。然后,您可以按照您建议的方式处理AST

在标记化模块中,甚至还有第一步的配方。为了避免只提供链接的答案,以下是该配方的代码(以及配方本身缺少的必要导入):

原始配方通过检查值中是否存在
来识别浮点文字。这并不完全是防弹的,因为它不包括像
'1e10'
这样的文字,还包括像
1.0j
这样的虚构文字(您可能想排除这些文字)。我在上面的
is\u float\u literal
中用我自己的版本替换了这个检查

在示例字符串上尝试此操作,我得到以下结果:

>>> expr = '100.50*num*discount'
>>> decistmt(expr)
"Decimal ('100.50')*num *discount "
。。。您现在可以像以前一样将其解析为AST树:

>>> tree = ast.parse(decistmt(expr), mode='eval')
>>> # walk the tree to validate, make changes, etc.
... 
>>> ast.dump(tree)
"Expression(body=BinOp(left=BinOp(left=Call(func=Name(id='Decimal', ...
最后评估:

>>> from decimal import Decimal
>>> locals = {'Decimal': Decimal, 'num': 3, 'discount': Decimal('0.1')}
>>> eval(compile(tree, 'dummy.py', 'eval'), locals)
Decimal('30.150')

您能解释一下源代码中的原始数字文字是什么意思吗?对不起,应该是数字文字:
>>> from decimal import Decimal
>>> locals = {'Decimal': Decimal, 'num': 3, 'discount': Decimal('0.1')}
>>> eval(compile(tree, 'dummy.py', 'eval'), locals)
Decimal('30.150')