Python中Decimal的子类化

2024-10-01 17:33:21 发布

您现在位置:Python中文网/ 问答频道 /正文

我想在Python程序中使用Decimal类进行财务计算。小数不能与浮点运算-它们需要先显式转换为字符串。 所以我决定将Decimal子类化,以便能够在不显式转换的情况下处理浮点。在

米_十进制.py公司名称:

# -*- coding: utf-8 -*-
import decimal

Decimal = decimal.Decimal

def floatCheck ( obj ) : # usually Decimal does not work with floats
    return repr ( obj ) if isinstance ( obj, float ) else obj # this automatically converts floats to Decimal

class m_Decimal ( Decimal ) :
    __integral = Decimal ( 1 )

    def __new__ ( cls, value = 0 ) :
        return Decimal.__new__ ( cls, floatCheck ( value ) )

    def __str__ ( self ) :
        return str ( self.quantize ( self.__integral ) if self == self.to_integral () else self.normalize () ) # http://docs.python.org/library/decimal.html#decimal-faq

    def __mul__ ( self, other ) :
        print (type(other))
        Decimal.__mul__ ( self,  other )

D = m_Decimal

print ( D(5000000)*D(2.2))

所以现在我可以不写D(5000000)*D(2.2)而不写D(5000000)*2.2,而不必引发异常。在

我有几个问题:

  1. 我的决定会给我带来麻烦吗?

  2. __mul__的情况下,重新实现__mul__不起作用,因为另一个参数的类型是class '__main__.m_Decimal',但是在decimal模块中可以看到这个:

在十进制.py,第5292行:

^{pr2}$

decimal模块要求参数是decimal或int。这意味着我应该先将m\u decimal对象转换为string,然后再转换为decimal。但是这是一个很大的浪费-m_Decimal是Decimal的后代-我如何使用它使类更快(Decimal已经非常慢了)。在

  1. 当cDecimal出现时,这个子类化能工作吗?在

Tags: pyselfobjreturnifdef情况else
3条回答

使用cdecimal或十进制.py从2.7或3.2。所有这些都有从浮点数 类方法:


class MyDecimal(Decimal):
    def convert(self, v):
        if isinstance(v, float):
            return Decimal.from_float(v)
        else:
            return Decimal(v)
    def __mul__(self, other):
        other = self.convert(other)
        return self.multiply(other)

目前,它根本做不到你想要的。您不能将m_decimal乘以任何值:由于缺少return语句,它将始终返回None:

    def __mul__ ( self, other ) :
        print (type(other))
        return Decimal.__mul__ ( self,  other )

即使加入了return,您仍然不能执行D(500000)*2.2,因为浮点仍然需要在十进制之前转换为十进制。mul将接受它。同样,repr在这里也不合适:

^{pr2}$

我的方法是创建一个classmethod,fromfloat

    @classmethod
    def fromfloat(cls, f):
        return cls(str(f))

然后重写mul方法以检查other的类型,并运行m_十进制.fromfloat()如果是浮点数:

class m_Decimal(Decimal):
    @classmethod
    def fromfloat(cls, f):
        return cls(str(f))

    def __mul__(self, other):
        if isinstance(other, float):
            other = m_Decimal.fromfloat(other)
        return Decimal.__mul__(self,other)

然后它会像你期望的那样工作。我个人不会重写新方法,因为使用fromfloat()方法对我来说似乎更简洁。但这只是我的看法。在

正如德克所说,您不需要担心转换,因为isinstance可以处理子类。您可能遇到的唯一问题是Decimal*m_Decimal将返回一个十进制值,而不是您的子类:

>>> Decimal(2) * m_Decimal(2) * 2.2

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    Decimal(2) * m_Decimal(2) * 2.2
TypeError: unsupported operand type(s) for *: 'Decimal' and 'float'

有两种方法可以解决这个问题。首先,将显式转换添加到m_Decimal的mul magicmethod:

    def __mul__(self, other):
        if isinstance(other, float):
            other = m_Decimal.fromfloat(other)
        return m_Decimal(Decimal.__mul__(self,other))

另一种方法,我可能不推荐,是“Monkeypatch”十进制模块:

decimal._Decimal = decimal.Decimal
decimal.Decimal = m_Decimal

在我看来,你根本不应该使用浮动。浮动不是金融应用程序的正确工具。在任何使用浮点的地方,都应该能够使用str或Decimal来确保不会丢失精度。在

例如。
用户输入,文件输入-显然使用str并转换为十进制来进行任何运算
数据库-如果支持,请使用decimal类型,否则使用字符串并在应用程序中转换为decimal。在

如果您坚持使用float,请记住python float在许多其他平台上等同于double,因此,如果您要将它们存储在数据库中,请确保数据库字段的类型为double

相关问题 更多 >

    热门问题