4

How can I set up multiple inheritance in python using class constructors with arguments? I've seen many examples with no arg constructors but that's no fun... I've done this setup in c++. I am learning python and figured I would try to recreate the same setup.

How can I get my Student_Worker constructor to work without all the *args,*kwargs bs?

If override works from Person to Student and to Worker why doesn't it work from Student and Worker to Student_Worker?

If the diamond case works on classes with no arguments in any of 4 constructors why wouldn't it work in a diamond case that has arguments in the constructors?

Am I just missing something simple? I can't find a decent guide anywhere.

class Person:        #BASE CLASS
    num_of_persons = 0;

    def __init__(self,x,y,z):     
        Person.num_of_persons += 1   
        print("Greetings!")
        self.Name = x
        self.Sex = y
        self.DOB = z
        print("I am " + self.Name)    

    def get_info(self):       
        return '{} {} {}'.format(self.Name,self.Sex,self.DOB)


class Student(Person): #DERIVED CLASS
    def __init__(self, x, y, z, s):
        self.school = s
        return super().__init__(x, y, z)

    def get_info(self):
        return super().get_info() + ' {}'.format(self.school)


class Worker(Person):  #DERIVED CLASS
    def __init__(self, x, y, z, c):
        self.company = c
        return super().__init__(x, y, z)

    def get_info(self):
        return super().get_info() + ' {}'.format(self.company)



class Student_Worker(Student, Worker): #MULTIPLE DERIVED CLASS
    def __init__(self,x,y,z,s,c):
        Student.__init__(x,y,z,c)
        Worker.__init__(c)


p1 = Person("John","M", "1900")
s1 = Student("Sam","F","1910","iSchool")
w1 = Worker("Larry","M","1920","E-SITE")
sw1 = Student_Worker("Brandy","F","1930","iSchool","E-Site")

print(p1.get_info())
print(s1.get_info())
print(w1.get_info())
print(sw1.get_info())
Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
Jjjj Gggg
  • 43
  • 2
  • Also... probably bad title – Jjjj Gggg Nov 14 '17 at 17:27
  • Is [this](https://stackoverflow.com/a/25352819) the "*args,*kwargs bs" you refer to? – Blorgbeard Nov 14 '17 at 17:30
  • "How can I get my Student_Worker constructor to work without all the *args,*kwargs bs?" - either deal with that BS, or don't use multiple inheritance. As annoying as you may find it, trying to use positional arguments with multiple inheritance in Python is far too fragile and messy to be worth doing. – user2357112 Nov 14 '17 at 17:31
  • Unfortunately python's new-style MRO forces you to jump through `*args, **kwargs` hoops to achieve diamond inheritance. With an old-style MRO, this would be a simple matter of passing the necessary arguments to the parent constructors. – Aran-Fey Nov 14 '17 at 17:35
  • 1
    @Rawing: Except that old-style would've led to double-initialization, and every `Student_Worker` would count as two people in `Person.num_of_persons`, so the old style didn't really handle this better. – user2357112 Nov 14 '17 at 17:38

1 Answers1

4

You can try creating the constructors as following:

1) Student

class Student(Person): #DERIVED CLASS
    def __init__(self, x, y, z, s):
        Person.__init__(self,x, y, z)
        self.school = s

2) Worker

class Worker(Person):  #DERIVED CLASS
    def __init__(self, x, y, z, c):
        Person.__init__(self,x, y, z)
        self.company = c

3) Student Worker

class Student_Worker(Student, Worker): #MULTIPLE DERIVED CLASS
    def __init__(self,x,y,z,s,c):
        Student.__init__(self,x,y,z,s)
        Worker.__init__(self,x,y,z,c)

If you run your code, you will get this output:

Greetings!
I am John
Greetings!
I am Sam
Greetings!
I am Larry
Greetings!
I am Brandy
Greetings!
I am Brandy
John M 1900
Sam F 1910 iSchool
Larry M 1920 E-SITE
Brandy F 1930 E-Site iSchool
Vasilis G.
  • 7,556
  • 4
  • 19
  • 29
  • he shouldnt need to alter his `Student` or `Worker` class at all ... simply properly invoking their constructors in the `Student_Worker` subclass should be sufficient – Joran Beasley Nov 14 '17 at 17:44
  • 1
    Will look into it. Thanks for clarifying. – Vasilis G. Nov 14 '17 at 17:45
  • 1
    @JoranBeasley I had to hack Student to get this working as well. Can you comment on this code? https://repl.it/repls/JollyNecessaryEagle – Blorgbeard Nov 14 '17 at 17:47
  • Commenting, like trying to fix it? – Vasilis G. Nov 14 '17 at 17:49
  • Sorry, I should be more explicit. I want to know why line 19 causes an exception, but replacing it with the commented line 20 fixes it - and why a similar replacement is not necessary on line 29. – Blorgbeard Nov 14 '17 at 17:51
  • @Blorgbeard hmm aparently i misspoke ... :P sorry my bad ... sorry for confusion – Joran Beasley Nov 14 '17 at 17:51
  • That's ok. Just give me a moment. – Vasilis G. Nov 14 '17 at 17:52
  • 1
    upvoted this answer :) (but as an aside add `Person.num_of_persons -= 1`) – Joran Beasley Nov 14 '17 at 17:53
  • Doesn't this solution cause `Person.num_of_persons` to be too high when you create a `Student_Worker`? – Barmar Nov 14 '17 at 17:57
  • @Joran Beasley is right, this will increase the `num_of_persons` more than it needs to, so. As for the answer suggested, I am still trying to understand why it doesn't works when using `super()`. If I don't manage to have an explanation, I will delete this answer as it's not properly justified. – Vasilis G. Nov 14 '17 at 18:02
  • its because in python 3 it implies self ... self inherits from both of those subclasses ... you are overthinking it ... its good with the explicit `__init__(self` – Joran Beasley Nov 14 '17 at 18:05
  • see this ... it may help you understand ... maybe https://repl.it/repls/SnarlingJuniorEagle – Joran Beasley Nov 14 '17 at 18:09
  • I see.. MRO is just a list, not a graph, so `Student_Worker`'s MRO lookup causes `super()` to resolve to `Worker` when called from `Student`. – Blorgbeard Nov 14 '17 at 18:33
  • changing `super()` to the `Person.__init__(self...)` in `Student` and `Worker` did the trick. Although I don't think I need to change the order of `self.school` and the base class call, it still worked without order change. – Jjjj Gggg Nov 14 '17 at 18:42
  • Had to remove `get_info` from `Student_Worker` to avoid double printing `school` and `company`. I did find it odd though that printing `Student_Worker` info gave company before school even though `Student` is before `Worker` in MRO? – Jjjj Gggg Nov 14 '17 at 18:46
  • Reading about MRO made it clear of how it works. Thank you for helping me and glad I could help. – Vasilis G. Nov 14 '17 at 19:06