finite graygreen
finite graygreen

Reputation: 335

Initialize a superclass with an existing object (copy constructor)

Preface: From my understanding the existing answers to this question assume control over the source or work around the problem.

Given a Super class, and MyClass which derives from it: How can an existing instance of a Super class be used as a base? The goal is to not call super().__init__() with fields from existing_super, but use the existing object. Similar to how in C++ a copy constructor would be used. Only MyClass can be adapted, Super has to remain unchanged.

class Super:
    def __init__(self, constructed, by, the, super_factory):
        """ no `existing_super` accepted """
        pass


class MyClass(Super):
    def __init__(self, existing_super):
        """ ????? """
        pass


s = super_factory()

mine = MyClass(s)

If this is not possible, would monkey patching Super help? What if Super uses/doesn't use slots?

Upvotes: 1

Views: 1159

Answers (3)

jsbueno
jsbueno

Reputation: 110506

Python has a somewhat "extra official" "copy" protocol, which can take care of copying classes instances, and which does take care of __slots__ and regular, contained in __dict__ attributes for you.

That said, if your derived class doesn't modify the slots, you can assign to the __class__ attribute, and just change a copy of the superclass instance into a subclass instance.

Then it is a matter of refactoring the subclass __init__ to set any new attributes you may have on the subclass.

def SuperCls:
    __slots__ = ... # or whatever
    def __init__(self, a, b):
        self.a = a
        self.b = b

from copy import copy

def MyClass:
    def __init__(self, a, b, c, d):
        super().__init__(a, b)
        self.sub_init(c, d)
    def sub_init(self, c, d):
        self.c = c
        self.d = d

    @classmethod 
    def myclass_from_super(cls, superinstance, c, d):
         new_inst = copy(superinstance) 
         new_inst.__class__ = cls
         new_inst.sub_init(c, d)
         return new_inst

I don't get from the question if you want to change the Superclass itself - i.e. the superclass instances could be instances of different copies. As far as the class layout (for pure-python code that means not changing the __slots__ ) doesn't change, this code will also work across different super-classes.

Upvotes: 0

ShainaR
ShainaR

Reputation: 1

I thought on this same problem for too long. This appears to be the solution.

class ObjectA:
   def __init__(self, a, b):
       self.a = a
       self.b = b

class ObjectB(ObjectA):
   def __init__(self, instanceA: ObjectA):
       super().__init__(**instanceA.__dict__)

# instance of parent class created
y = ObjectA(a=1,b=2)
# instance of parent class used to initialize child class
x = ObjectB(y)
# child class successfully inherits attributes of parent
x.a

Upvotes: 0

nategriff
nategriff

Reputation: 11

One way to approach it is to convert your instance of the super class into an instance of the subclass using the __class__ attribute.

class A:
    def __init__(self):
        self.A_attribute = 'from_A'


class B(A):
    def __init__(self):
        #Create an instance of B from scratch
        self.B_attribute = 'from_B'
        super().__init__()

    def init_A(self):
        #Initializes an instance of B from what is already an instance of A
        self.B_attribute = 'from_B'

myA = A()
myA.__class__ = B
myA.init_A()

Upvotes: 1

Related Questions