1

I am looking for a solution to allow me to have dynamic inheritance of classes based on certain conditions being met (Python 3.6). Simple enough it seems, but I can't get the attributes of the parent classes to be available in the child class. Everything that depends on self either yields a missing argument error or the attribute doesn't appear. I implemented the solutions to the problem given here and here for dynamic inheritance, yet still run into the same problem with attributes of the child class.

For a sample:

class Parent:
    def __init__(self):
        self.some_value = 1

    def some_function(self):
        return self.some_value

def classFactory(parent):
    class child(parent):
        def __init__(self, parent):
            super(child, self).__init__()
            parent.__init__(self)
            self.some_other_value = 2

        def some_other_function(self):
            return self.some_value + self.some_other_value
    return child

child_class = classFactory(Parent)

child_class.some_value
AttributeError: type object 'child' has no attribute 'some_value'

child_class.some_other_value
AttributeError: type object 'child' has no attribute 'some_other_value'

child_class.some_other_function()
TypeError: some_other_function() missing 1 required positional argument: 'self'

However, if I take the same child construction and remove it from the function definition, it works.

class child(Parent):
    def __init__(self, parent):
        super(child, self).__init__()
        parent.__init__(self)
        self.some_other_value = 2

    def some_other_function(self):
        return self.some_value + self.some_other_value

child_class = child(Parent)
print(child_class.some_value)
# 1
print(child_class.some_other_value)
# 2
print(child_class.some_other_function())
# 3

Why is it that the attributes aren't being inherited in the first case but they are in the second? How can I write the dynamic inheritance to give me the behaviour I expect (as shown in the second case)?

hubbs5
  • 1,235
  • 1
  • 12
  • 22
  • 2
    What problem are you trying to solve by doing this? There's probably a better way to solve it. – user2357112 Feb 05 '19 at 00:33
  • I do not believe you need to explicitly call the constructor on `Parent` if using `super` function. – fstop_22 Feb 05 '19 at 01:15
  • The errors result from attempting to use the class instead of an instance of that class. – Dan D. Feb 05 '19 at 01:24
  • why are you calling `super.__init__` and `parent.__init__`? – juanpa.arrivillaga Feb 05 '19 at 01:26
  • 2
    Anyway, you are getting attribute errors because those are *instance atttributes* that belong to instances, not the their classes. You never instantiate an instance in your first example. – juanpa.arrivillaga Feb 05 '19 at 01:27
  • I have a series of optimization algorithms (parent classes) with different parameters that need to make decisions in a simulation. I'm trying to build a child class to provide a consistent interface between the various algorithms and the simulation piece and it must have access to the details of the particular algorithms, which is defined at runtime. I'm sure there are different ways to do this, my thought was that this would be the most straightforward. – hubbs5 Feb 05 '19 at 14:33
  • The `super.__init__` is left over from when I was simply trying a bunch of different things to make it work. I think it's redundant. – hubbs5 Feb 05 '19 at 14:35

2 Answers2

0

It works if I instantiate the child class with the parent argument in return child(parent). This preserves the attributes and methods of both the parent and child.

class Parent:
    def __init__(self):
        self.some_value = 1

    def some_function(self):
        return self.some_value

def classFactory(parent):
    class child(parent):
        def __init__(self, parent):
            parent.__init__(self)
            self.some_other_value = 2

        def some_other_function(self):
            return self.some_value + self.some_other_value
    return child(parent)

child_class = classFactory(Parent)

print(child_class.some_value)
# 1
print(child_class.some_other_value)
# 2    
print(child_class.some_other_function())
# 3
print(child_class.some_function())
# 1
hubbs5
  • 1,235
  • 1
  • 12
  • 22
0

The most explicit implementation of dynamic inheritance may be implemented with metaclasses:

class A:pass

class B:pass


class Meta(type):

     def __new__(cls, name, bases, dct):
         bases = (A,) if Meta.condition() else (B,)
         return type(name, bases, dct)

     @staticmethod
     def condition():
         # implement condition.
         ...

class C(metaclass=Meta):
    pass

c_instance = C()
print("is C instance of A:")
print(isinstance(c_instance, A))
print("is C instance of B:")
print(isinstance(c_instance, B))

Alternatively, you can define condition function and it as following:


condition = lambda: True
class D(A if condition() else B):
    pass

d_instance = D()
print("is D instance of A:")
print(isinstance(d_instance, A))
print("is C instance of B:")
print(isinstance(d_instance, B))

The approach with metaclasses will give you more control over the class creation, so it depends on your needs ...

Andriy Ivaneyko
  • 20,639
  • 6
  • 60
  • 82