0

I'm trying to inherit Decimal and create a math class than can handle polymorphic input. The problem I'm running into is that nothing I do seems to allow me to create duplicate instances.

I've looked at dozens of examples, but nothing I've found actually creates multiple instances from an internal constructor. They all do it from outside.

To have overridden math functions with polymorphic input, the instance-constructor MUST be internal to the class. There is no other way. I've been over this ten different ways, looked at tons of online docs ( mostly flat out wrong ) Hopefully somebody here can help.

#
### FOO.py

import sys

from decimal import Decimal, getcontext
import socket
import operator
import re
import copy

class Amount(Decimal): 

    def __init__(self):  

        instancehex = hex(id(self)) 
        print ("class: ", self.__class__.__name__)
        print ("baseclass: ", self.__class__.__base__)
        print ("instance: ", instancehex)

        super(type(self),self).__init__() # inherit decimal for getcontext()

        print ("postinitclass: ", self.__class__.__name__)
        print ("postinitbaseclass: ", self.__class__.__base__)
        print ("postinitinstance: ", instancehex)

        # these are instance variables? If not, how from inside an instance-constructor? 

        self.value = Decimal(0.0) # our initial value
        self.isdefined = False # 

    def newamount(self, *arg): 

        # this should call __init__? Doesn't happen. 

        thisobject = copy.deepcopy(self)

        # these should be different memory addresses? 

        selfhex = hex(id(self)) 
        thishex = hex(id(thisobject))

        print ("\tself: ", str(selfhex))
        print ("\tcopy: ", str(thishex))

        if len(arg): 
            polyinput = arg[0]
            thisobject.setvalue(polyinput)
            self.isdefined = True

        return(thisobject)

    def setvalue(self,polyinput): 

        # polymorphic numeracy 

        print ("\t\tsetting from: ", polyinput.__class__.__name__)

        if polyinput.__class__.__name__ == "Amount": 
            self.value = (polyinput.value)
        elif polyinput.__class__.__name__ == "float": 
            self.value.from_float(polyinput)

    def __add__(self,polyinput):
        rcvamount = self.newamount(polyinput) 
        sumamount = self.newamount() 

        print ("ADDING:")

        if rcvamount.isdefined and self.isdefined: 
            print ("\tdefined values", str(self.value), ":", str(rcvamount.value))

            # super() is magical with operator intercepts? 

            sumamount.value = self.value + rcvamount.value 

        else: 
            assert False, "cannot add undefined values"

        return (sumamount)
#
### testfoo.py 

import sys
from decimal import Decimal,getcontext

import socket
import operator
import re

import FOO

# set the class currency type

Factory = FOO.Amount()

print ("Amounts: ")
m0 = Factory.newamount(0.1) 
m1 = Factory.newamount(0.02) 
m2 = Factory.newamount(0.03) 

print ("REPORT: ",  m0.__class__, type(m0).__name__, hex(id(m0)), m0)
print ("REPORT: ", m1.__class__, type(m1).__name__, hex(id(m0)), m1)
print ("REPORT: ", m2.__class__, type(m2).__name__, hex(id(m0)), m2)

m3 = m2 + m1
print (type(m3).__name__, hex(id(m3)), m3)

print (m1,":", m2)
print (m1,":",m2,":",m3)
#
class:  Amount
baseclass:  <class 'decimal.Decimal'>
instance:  0x7f821c830ca8
postinitclass:  Amount
postinitbaseclass:  <class 'decimal.Decimal'>
postinitinstance:  0x7f821c830ca8
Amounts: 
    self:  0x7f821c830ca8
    copy:  0x7f821c830ca8
        setting from:  float
    self:  0x7f821c830ca8
    copy:  0x7f821c830ca8
        setting from:  float
    self:  0x7f821c830ca8
    copy:  0x7f821c830ca8
        setting from:  float
REPORT:  <class 'FOO.Amount'> Amount 0x7f821c830ca8 0
REPORT:  <class 'FOO.Amount'> Amount 0x7f821c830ca8 0
REPORT:  <class 'FOO.Amount'> Amount 0x7f821c830ca8 0
    self:  0x7f821c830ca8
    copy:  0x7f821c830ca8
        setting from:  Amount
    self:  0x7f821c830ca8
    copy:  0x7f821c830ca8
ADDING:
    defined values 0 : 0
Amount 0x7f821c830ca8 0
0 : 0
0 : 0 : 0
1
  • You can define a __deepcopy__ method to customize how deepcopy works with your class. See an example here Commented Sep 2, 2019 at 19:02

1 Answer 1

1

I believe you are going about this the wrong way. You have effectively created a copy of the __init__ method in newamount. You would be better off subclassing Decimal and then just overloading the methods you want to use.

For example, you can create a method to check the type of another object and pass it to Amount if it is not an instance of Decimal. Then overloading the __add__ and __radd__ methods allows the Amount object accept polymorphic addition.

from collections.abc import Iterable
from decimal import Decimal

class Amount(Decimal):
    def __repr__(self):
        return "Amount({})".format(self.__float__())

    def _checkset(self, other):
        if isinstance(other, Decimal):
            return other
        if isinstance(other, Iterable):
            return Amount(other[0])
        return Amount(other)

    def __add__(self, other):
        return self.__class__(super().__add__(self._checkset(other)))

    def __radd__(self, other):
        return self + other

a = Amount(1.2)

a + .4
# returns:
Amount(1.5999999999999999)

1 + a
# returns:
Amount(2.2)

# in your code, iterables only take the first object
a + [2, 3, 4]
# returns:
Amount(3.2)
Sign up to request clarification or add additional context in comments.

5 Comments

Could you explain the line: return "Amount({})".format(self.__float__()) I've never seen this form before.
That is the new string formatting minilanguage. docs.python.org/3/library/string.html#formatspec
This still doesn't work. I can't get bring the superclass handle into a property without a type conflict, and I can't set getcontext().rounding or getcontext().prec because the method you've described would make that double nested perens syntax error. So none of the contexts are accessible internally. with the solution above. So it is still catch22.
holdon. I might have fixed that. God I hate python right now.
Got it working I think. Thanks so much! I rebuilt this class a dozen times at least, got it working (I thought) twice, before something else required a tweak that blew it up again. I still don't think I get it. I just don't get why I can't call a constructor from within a classes namespace, except (apparently) from within an anonymous string object?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.