我是编程的新手,所以我想在这里寻求帮助。 所以当我使用:
eval('12.5 + 3.2'),
它将12.5和3.2转换为浮点数。 但是我希望将它们转换为Decimal数据类型。
我可以使用:
from decimal import Decimal
eval(Decimal(12.5) + Decimal(3.2))
但是我在接受用户输入时无法在程序中做到这一点。
我找到了一个解决方案,但是它使用了正则表达式,我现在不熟悉它(由于某种原因,我无法再次找到它。)
如果有人可以帮助我,那将是很棒的。谢谢!
答案 0 :(得分:7)
更新:显然,官方文档的配方完全可以满足您的需求。来自https://docs.python.org/3/library/tokenize.html#examples:
from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP
from io import BytesIO
def decistmt(s):
"""Substitute Decimals for floats in a string of statements.
>>> from decimal import Decimal
>>> s = 'print(+21.3e-5*-.1234/81.7)'
>>> decistmt(s)
"print (+Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7'))"
The format of the exponent is inherited from the platform C library.
Known cases are "e-007" (Windows) and "e-07" (not Windows). Since
we're only showing 12 digits, and the 13th isn't close to 5, the
rest of the output should be platform-independent.
>>> exec(s) #doctest: +ELLIPSIS
-3.21716034272e-0...7
Output from calculations with Decimal should be identical across all
platforms.
>>> exec(decistmt(s))
-3.217160342717258261933904529E-7
"""
result = []
g = tokenize(BytesIO(s.encode('utf-8')).readline) # tokenize the string
for toknum, tokval, _, _, _ in g:
if toknum == NUMBER and '.' in tokval: # replace NUMBER tokens
result.extend([
(NAME, 'Decimal'),
(OP, '('),
(STRING, repr(tokval)),
(OP, ')')
])
else:
result.append((toknum, tokval))
return untokenize(result).decode('utf-8')
然后您可以像这样使用:
from decimal import Decimal
s = "12.5 + 3.2 + 1.0000000000000001 + (1.0 if 2.0 else 3.0)"
s = decistmt(s)
print(s)
print(eval(s))
结果:
Decimal ('12.5')+Decimal ('3.2')+Decimal ('1.0000000000000001')+(Decimal ('1.0')if Decimal ('2.0')else Decimal ('3.0'))
17.7000000000000001
随意跳过此答案的其余部分,现在只有半正确解的历史学家才感兴趣。
据我所知,没有简单的方法可以“挂钩” eval来更改其解释浮动对象的方式。
但是,如果我们在ast
之前使用eval
模块将您的字符串转换为抽象语法树,那么我们可以操纵该树,以Decimal调用替换浮点数。
import ast
from decimal import Decimal
def construct_decimal_node(value):
return ast.Call(
func = ast.Name(id="Decimal", ctx=ast.Load()),
args = [value],
keywords = []
)
return expr
class FloatLiteralReplacer(ast.NodeTransformer):
def visit_Num(self, node):
return construct_decimal_node(node)
s = '12.5 + 3.2'
node = ast.parse(s, mode="eval")
node = FloatLiteralReplacer().visit(node)
ast.fix_missing_locations(node) #add diagnostic information to the nodes we created
code = compile(node, filename="", mode="eval")
result = eval(code)
print("The type of the result of this expression is:", type(result))
print("The result of this expression is:", result)
结果:
The type of the result of this expression is: <class 'decimal.Decimal'>
The result of this expression is: 15.70000000000000017763568394
如您所见,结果与直接计算Decimal(12.5) + Decimal(3.2)
所得到的结果相同。
但是也许您在想“为什么结果15.7不?”。这是因为Decimal(3.2)
与3.2不完全相同。它实际上等于3.20000000000000017763568394002504646778106689453125
。当使用浮点对象初始化小数时,这是一种危险-不准确已经存在。最好使用字符串来创建小数,例如Decimal("3.2")
。
也许您现在正在思考“好吧,那我该如何将12.5 + 3.2
变成Decimal("12.5") + Decimal("3.2")
?”。最快的方法是修改construct_decimal_node
,以便Call的args是ast.Str而不是ast.Num:
import ast
from decimal import Decimal
def construct_decimal_node(value):
return ast.Call(
func = ast.Name(id="Decimal", ctx=ast.Load()),
args = [ast.Str(str(value.n))],
keywords = []
)
return expr
class FloatLiteralReplacer(ast.NodeTransformer):
def visit_Num(self, node):
return construct_decimal_node(node)
s = '12.5 + 3.2'
node = ast.parse(s, mode="eval")
node = FloatLiteralReplacer().visit(node)
ast.fix_missing_locations(node) #add diagnostic information to the nodes we created
code = compile(node, filename="", mode="eval")
result = eval(code)
print("The type of the result of this expression is:", type(result))
print("The result of this expression is:", result)
结果:
The type of the result of this expression is: <class 'decimal.Decimal'>
The result of this expression is: 15.7
但是要当心:虽然我希望这种方法在大多数情况下都能返回良好的结果,但在极端情况下它会返回令人惊讶的结果。特别是当表达式包含浮点数f
这样的float(str(f)) != f
时。换句话说,当浮标的打印表示形式缺乏精确表示浮标所需的精度时。
例如,如果将以上代码中的s
更改为"1.0000000000000001 + 0"
,则结果将为1.0
。这是不正确的,因为Decimal("1.0000000000000001") + Decimal("0")
的结果是1.0000000000000001
。
我不确定如何解决此问题... ast.parse
执行完毕时,float文字已被转换为float对象,并且没有明显的方法来检索字符串那是用来创建它的。也许您可以从表达式字符串中提取它,但是从根本上讲,您必须重新发明Python的解析器才能做到这一点。