I want to use Decimal class in my Python program for doing financial calculations. Decimals to not work with floats – they need explicit conversion to strings first.
So i decided to subclass Decimal to be able to work with floats without explicit conversions.
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))
So now instead of writing D(5000000)*D(2.2) i should be able to write D(5000000)*2.2 without rasing exceptions.
I have several questions:
-
Will my decision cause me any troubles?
-
Reimplementing
__mul__doesn’t work in case ofD(5000000)*D(2.2), because the other argument is of typeclass '__main__.m_Decimal', but you can see in decimal module this:
decimal.py, line 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
The decimal module expects argument being Decimal or int. This means i should convert my m_Decimal object to string first, then to Decimal. But this is lot of waste – m_Decimal is descendant of Decimal – how can i use this to make the class faster (Decimal is already very slow).
- When cDecimal will appear, will this subclassing work?
Currently, it won’t do what you want at all. You can’t multiply your m_decimal by anything: it will always return None, due to a missing return statement:
Even with the return added in, you still can’t do D(500000)*2.2, as the float still needs converting to a Decimal, before Decimal.mul will accept it. Also, repr is not appropriate here:
The way I would do it, is to make a classmethod, fromfloat
Then override the mul method to check for the type of other, and run m_Decimal.fromfloat() on it if it is a float:
It will then work exactly as you expect. I personally wouldn’t override the new method, as it seems cleaner to me to use the fromfloat() method. But that’s just my opinion.
Like Dirk said, you don’t need to worry about conversion, as isinstance works with subclasses. The only problem you might have is that Decimal*m_Decimal will return a Decimal, rather than your subclass:
There are two ways to fix this. First is to add an explicit conversion to the m_Decimal’s mul magicmethod:
The other way, which I probably wouldn’t recommend, is to “Monkeypatch” the decimal module: