4

我想在我的 Python 程序中使用 Decimal 类进行财务计算。不能与浮点数一起使用的小数 - 它们需要首先显式转换为字符串。所以我决定继承 Decimal 以便能够在没有显式转换的情况下使用浮点数。

m_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__不起作用D(5000000)*D(2.2),因为另一个参数是 type class '__main__.m_Decimal',但是您可以在十进制模块中看到:

十进制.py,第 5292 行:

def _convert_other(other, raiseit=False):
    """Convert other to Decimal.

    Verifies that it's ok to use in an implicit construction.
    """
    if isinstance(other, Decimal):
        return other
    if isinstance(other, (int, long)):
        return Decimal(other)
    if raiseit:
        raise TypeError("Unable to convert %s to Decimal" % other)
    return NotImplemented

十进制模块期望参数是十进制或整数。这意味着我应该先将我的 m_Decimal 对象转换为字符串,然后再转换为十进制。但这很浪费 - m_Decimal 是 Decimal 的后代 - 我如何使用它来使课程更快(Decimal 已经很慢了)。

  1. 当 cDecimal 出现时,这个子类化会起作用吗?
4

3 回答 3

4

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

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

即使添加了返回值,您仍然无法执行 D(500000)*2.2,因为浮点数仍需要在十进制之前转换为十进制。mul会接受的。另外, repr 在这里也不合适:

>>> repr(2.1)
'2.1000000000000001'
>>> str(2.1)
'2.1'

我会这样做的方式是制作一个类方法,fromfloat

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

然后重写 mul 方法以检查 other 的类型,如果它是浮点数,则对其运行 m_Decimal.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() 方法对我来说似乎更干净。但那只是我的个人意见。

就像 Dirk 所说,您不必担心转换,因为 isinstance 与子类一起使用。您可能遇到的唯一问题是 Decimal*m_Decimal 将返回 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 魔术方法添加显式转换:

    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
于 2010-01-11T22:07:19.260 回答
1

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

例如。
用户输入,文件输入 - 显然使用 str 并转换为 Decimal 来执行任何数学
数据库 - 如果支持,则使用 decimal 类型,否则使用字符串并在您的应用程序中转换为 Decimal。

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

于 2010-01-11T23:22:06.157 回答
0

使用 2.7 或 3.2 中的 cdecimal 或 decimal.py。所有这些都有 from_float 类方法:


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)
于 2010-01-14T00:56:31.627 回答