0

I have a python class that serves as a baseclass for further subclasses. It contains a methods that should act on all subclass the same, e.g. I want to put it in the base class. The problem is, that this method should return a new instance of the subclass. But I since the baseclass is located before the definition of the subclass, I can not create a new instance of the subclass, as it is not known in the scope of the baseclass:

class Base:
  def __init__(self, value):
    self.value = value

  def convert_child_classes(self, newclass):
    newvalue = MakeUsableInNewclass(self.value)
    newattr = MakeUsableInNewclass(self.subclassattr)
    return newclass(newattr, newvalue)


class Child1(Base):
  def __init__(self, subclassattr, value)
    super(Child, self).__init__(value)
    self.subclassattr = subclassattr

  def MethodForSubClassAttr(self):
    ...do sth with self.subclassattr...


class Child2(Base):
  def __init__(self, subclassattr, value)
    super(Child, self).__init__(value)
    self.subclassattr = subclassattr

  def SomeOtherSubClassAttrMethod(self):
    ...do sth that is related to this class attr...

I if I have an instance of Child1 I want to be able to do some stuff with its data and then return an instance of the Child2 with the new values when calling convert_child_classes(Child2):

A = Child1('someattr', 5)
B = A.convert_child_classes(Child2)

now B should be an instance of Child2 with a value that was calculated form Child1. But since Base class does now know what Child1 or Child2 is, it can not initiate the new class.

Torilla
  • 383
  • 5
  • 16
  • Which stuff are your talking exactly on doing, @Torilla? Because if you're using attributes from `Child1`, then you can just operate them from the `Base Class` without an issue. Regarding to instantiate `Class2`, you can receive the `Child2` as an argument, as you already do, but when you `return newclass(newvalue)`, you should add both argumentss from your `Child2 __init__` function, what it'd not shown here – Luan Naufal Dec 08 '18 at 12:08
  • the idea is that there are many childclasses of baseclass, but they are interrelated and can be converted into each other. For a practical example just think a Baseclass that represents length, and a child class that represents meter and one that represents yards and one inches. I want to be able, if I have an instance that represent 5 meters, that I can convert it to a Inch class for example. – Torilla Dec 08 '18 at 12:11
  • perfect! So you should use one metric as reference to your `BaseClass`. Now, for each `Child` instance, create a variable such as `self.conversion` that will store the conversion value between both metrics. Now, when you call `convert_child_classes`, make the calculation before assigning to the `new class`, using this `self.conversion` that all classes will have implemented, so you don't need to care which Class it's coming from. Does it make sense for you? – Luan Naufal Dec 08 '18 at 12:14
  • But if I call Child2(newattr, newvalue) i get an ValueError as the BaseClass des not know what Child2 . – Torilla Dec 08 '18 at 12:16
  • I would be inclined to do this by: (1) defining some canonical base unit (2) each class knows how to convert itself to base unit (3) each class has a factory classmethod which accepts other class, calls its `convert_to_base_unit` method, then creates an instance of itself from that value. – snakecharmerb Dec 08 '18 at 12:16
  • I got your problem, @Torilla, and just added the answer here below: https://stackoverflow.com/a/53682559/10642035 Just let me know if it solves your issue – Luan Naufal Dec 08 '18 at 12:32

2 Answers2

1

Something like this ought to work (untested):

class Measurement:

    @classmethod
    def from_other(cls, other):
        base = other.convert_to_base_unit()
        converted = cls.base_to_unit(base)
        return cls(converted)

    @classmethod
    def base_to_unit(cls, value):
        # Let the subclass implement this
        raise NotImplementedError

    def convert_to_base_unit(self):
        # Let the subclass implement this
        raise NotImplementedError

Implemented this way, the base class doesn't need to know anything about the subclasses. The base class provides the template method (from_other) and the subclasses provide the implementation.

snakecharmerb
  • 47,570
  • 11
  • 100
  • 153
1

I got your problem:
1. Actually you're using Child in the super and it's wrong, as it should be the name of the class you're operating on, in this case Child1 or Child2.
2. I'd add Base as an abstract class, to ensure it won't be instantiated (as I got from your question it's the case).
3. since the method MakeUsableInNewClass is mandatory to be implemented, I'd add as an abstractmethod to ensure implementation on the Child ones.

So the correct code would be:

from abc import ABC, abstractmethod
class Base(ABC):
    def __init__(self, value):
        self.value = value

    def convert_child_classes(self, newclass):
        newvalue, newattr = self.MakeUsableInNewclass()
        return newclass(newattr, newvalue)

    @abstractmethod
    def MakeUsableInNewclass(): pass

class Child1(Base):
    def __init__(self, subclassattr, value):
        super(Child1, self).__init__(value)
        self.subclassattr = subclassattr

    def MakeUsableInNewclass(self):
        newvalue = self.value #do operations
        newattr = self.subclassattr #do operations
        return newvalue, newattr

class Child2(Base):
    def __init__(self, subclassattr, value):
        super(Child2, self).__init__(value)
        self.subclassattr = subclassattr

    def MakeUsableInNewclass(self):
        newvalue = self.value #do operations
        newattr = self.subclassattr #do operations
        return newvalue, newattr
Luan Naufal
  • 1,346
  • 9
  • 15