James Aanderson
James Aanderson

Reputation: 330

How to create an instance with deepcopy in python?

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

Upvotes: 0

Views: 63

Answers (1)

James
James

Reputation: 36623

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)

Upvotes: 1

Related Questions