2019-04-03 20:53:36 (edited by amerikranian 2019-04-03 20:56:49)

So, while I'm not building a project that requires inheritance, I think it's time I at least start to wrap my head around the concept: Consider the following code, (python)

class test_class:
 def __init__(self):
  self.x=2
  self.y=4
  self.z=3
 def add_xnumber(self,num):
  self.x+=num
 def multiply_xnumber(self,num):
  self.x*=num
class second_test_class(testing_class):
 pass

The code above is dumb and pointless, but it does show my question in much more detail. What happened when I typed second_test_class(test_class)? Here's what I know about inheritance so far. Basically, it's like having a kid... without a second parent of course. What does the kid inherit? Does he invent the entirety of his dad's DNA (all the functions of the test_class)? Does he inherit the test_class's constructor? Can I have another constructor within my second class even though it already has one from inheritance? How many classes can I inherit from? How deep can the relationship go? I.e, if I do class third_test_class(second_test_class) will I inherit the second class's functions and the base constructor of test_class? Finally, what does super() mean in python? When typing it into the prompt, I see that it is of type super, and super.__doc__ doesn't make sense.
I've tried looking up tutorials, but all of the ones I've found didn't quite explain all of my questions, so I'm hoping that'll change here.

2019-04-04 01:50:56 (edited by magurp244 2019-04-04 02:25:34)

Inheritance is often referred to as a Parent/Child relationship, and you can have multiple inheritance from multiple classes, though by default it will inherit only the first classes constructor as long as the child doesn't have its own, but there are ways around this. Children will also inherit their parents functions, though duplicates get overwritten. You can also have multiple child classes inherit from each other, so parent A can have Child B, and Child C can be from Child B, and so forth and so on.

As for super, it can be used to refer to parent classes without naming them explicitly, so if you have a function of the same name thats different from the parents, you can call the parents function instead. Put another way, if your child class has a constructor, you can use super to call the parents constructor too, though its best to design the parents and child classes for this for multiple inheritance.

class mother(object):
    def __init__(self):
        self.x = 1
    def stuff(self):
        pass

class father(object):
    def __init__(self):
        self.y = 2
    def muff(self):
        pass

class kid(mother,father):
    def __init__(self):
        super(kid,self).__init__()
        self.z = 3

a = kid()
print(dir(a))

Another way to do multiple inheritance is to call the parents constructors manually in the childs constructor, like so:

class mother(object):
    def __init__(self):
        self.x = 1
    def stuff(self):
        pass

class father(object):
    def __init__(self):
        self.y = 2
    def muff(self):
        pass

class kid(mother,father):
    def __init__(self):
        mother.__init__(self)
        father.__init__(self)
        self.z = 3

a = kid()
print(dir(a))
-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2019-04-05 20:57:31

So just to confirm that I understand:
In your first example, you wrote:

        super(kid,self).__init__()

Does that call every single constructor the class inherated from? I.e, if I added a grandfather class to the inheritance list in the kid class, will it call the grandfather's constructor as well?
Let me make sure that I understand the params of the super() function.
1: The object which must inherit at least once from a class.
2: The object that possible variables could be assigned to, self in this case.
Finally, the kid has all the variables/functions mother and father had? I.e, it has x, y, and z, and it also has the stuff and muff functions, correct?

2019-04-06 02:08:03 (edited by magurp244 2019-04-06 06:15:06)

Super is a bit of a tricky function to use at times, I'm not entirely sure if there are differences between python 2 and python 3 with it. Let's try a different example here, every child class has an __mro__ attribute before its created, this tells you the order that the classes are called when initializing a class with single, or multiple inheritance. Now if the kid class has an __init__() constructor or a function of the same name as its parents, it will ignore those functions but inherit all the other functions and variables they have. So for example:

class mother(object):
    def __init__(self):
        self.x = 1
    def stuff(self):
        pass

class father(object):
    def __init__(self):
        self.y = 2
    def muff(self):
        pass

class kid(mother,father):
    def __init__(self):
        self.z = 3
    def stuff(self):
        print('stuff')
    def muff(self):
        print('muff')

a = kid()
print(dir(a))

kid in this case would inherit nothing from its parents because it already has an __init__(), stuff(), and muff() function. But you can still call its parent classes functions directly if you want to, such as __init__() like so:

class mother(object):
    def __init__(self):
        self.x = 1
    def stuff(self):
        pass

class father(object):
    def __init__(self):
        self.y = 2
    def muff(self):
        pass

class kid(mother,father):
    def __init__(self):
        self.z = 3
    def stuff(self):
        print('stuff')
        mother.__init__(self)
    def muff(self):
        print('muff')

a = kid()
a.stuff()
print(dir(a))

This gives the kid class its mothers variables from __init__(). Now the way super works involves the Method Resolution Order, or __mro__. Example:

class mother(object):
    def __init__(self):
        self.x = 1
    def stuff(self):
        pass

class father(object):
    def __init__(self):
        self.y = 2
    def muff(self):
        pass

class kid(mother,father):
    def __init__(self):
        self.z = 3

print(kid.__mro__)
a = kid()
(<class '__main__.kid'>, <class '__main__.mother'>, <class '__main__.father'>, <type 'object'>)

So this shows the order the classes are called in, kid, mother, father, and the type of object. When using super you need the class to call, the type, and the function to call. So super(kid,self).__init__() would call the class thats next in line in the mro list, which would be mother. If you called super(mother,self).__init__() it would call the next in line, which would be father. Not the most super intuitive compared to just calling the parent classes directly, but eh. Like I said, there may be some differences between python 2 and python 3, and nesting the super calls between parent and child classes can be better for chaining inheritance calls along a tree list class structure. There's some more information available [here] and [here].

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2019-04-06 23:53:06

@4, thank you for the help once again. I've also sent you a PM