Python Forum
[Classes] Class Intermediate: Inheritance
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Classes] Class Intermediate: Inheritance
#3
So all is well and good with our employee classes. But what if our business expands, and now we have so many field investigators we want to have some managers to make them less efficient? These new field managers would have salaries, but no location. If we were to make a new sub-class of StandardEmployee, we would have to duplicate the methods that Manager and Investigator overrode. Much of the point of inheritance is to allow us to reuse code.

To get around this, we're going to use multiple inheritance. Multiple inheritance is pretty much what it sounds like: one class inherits from more than one base classes. We already have the classes we need, we just need to make a new class for the field managers:

class FieldManager(Investigator, Manager):
   """
   A manager of field investigators.
   """
   pass
Simple, eh? And we can create an instance:

# exammple employee
tyler = FieldManager('Director of Invesigations', 'Tyler', 'Durden', '555-6732', 123456)
And it will work as expected:

>>> tyler.contact_info()
'Durden, Tyler (555-6732)'
>>> tyler.weekly_pay()
2374.0
Note that we didn't initialize Tyler with a location, so he must be taking __init__ from Investigator. He's also clearly taking the contact_info method from Investigator, but he's taking the weekly_pay method from Manager.

You might wonder if it matters what order we list the base classes in. If you create another version with the base classes reversed:

class FieldManager2(Manager, Investigator):
   """
   A manager of field investigators.
   """
   pass
   
# exammple employee
tyler2 = FieldManager2('Director of Invesigations', 'Tyler', 'Durden', '555-6732', 123456)
If we try it out, we get the exact same results:

>>> tyler2.contact_info()
'Durden, Tyler (555-6732)'
>>> tyler2.weekly_pay()
2374.0
That would seem to imply that the order doesn't matter. But this is a special case, because there is no conflict between Manager and Investigator. Neither one overrides a method that the other one does. If there's no conflict, the order doesn't matter, but if there is a conflict, the order does matter:

class Spam(object):
      
   def eggs(self):
      return 'spam'
      
   def ham(self):
      return 'spam'
   
   def me(self):
      return 'spam'
      
   def spam(self):
      return 'spam'
      
class Ham(Spam):
   
   def ham(self):
      return 'ham'
   
   def me(self):
      return 'ham'
      
class Eggs(Spam):
   
   def eggs(self):
      return 'eggs'
   
   def me(self):
      return 'eggs'

class HamAndEggs(Ham, Eggs):
   
   pass

class EggsAndHam(Eggs, Ham):
   
   pass
Here there's a conflict: both Ham and Eggs define a 'me' method.

>>> he = HamAndEggs()
>>> he.me()
'ham'
>>> eh = EggsAndHam()
>>> eh.me()
'eggs'
HamAndEggs is getting the me method from Ham (first in it's base class list), while EggsAndHam is getting it from Eggs (first in EggsAndHam's base class list). We can track down the whole order of how HamAndEggs searches when looking for a method (called the Method Resolution Order):

>>> he.me()
'ham'
>>> he.ham()
'ham'
>>> he.eggs()
'eggs'
>>> he.spam()
'spam'
There's three place it could get me (Spam, Eggs, and Ham), and it gets it from Ham. There's two places it could get ham (Ham and Spam), and it gets it from Ham. Likewise it gets Eggs from Eggs instead of Spam. So Ham is before Eggs, and both are before Spam. Of course it will search itself first, so the order is HamAndEggs, Ham, Eggs, Spam. You can check this with the __mro__ attribute of a class:

>>> HamAndEggs.__mro__
(<class '__main__.HamAndEggs'>, <class '__main__.Ham'>, <class '__main__.Eggs'>,
 <class '__main__.Spam'>, <class 'object'>)
I didn't account for object in my order, but it's in there at the end. Now this might just look like a breadth-first search of the tree of ancestors. In that case, the MRO of:

class Breakfast(HamAndEggs, EggsAndHam):
   pass
Would be (Breakfast, HamAndEggs, EggsAndHam, Ham, Eggs, Spam, object). But what you actually get is:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Ham, Eggs
Why you get that error and exactly how the MRO is calculated is really beyond the scope of this tutorial. If you're reading a tutorial on classes, you don't need to worry about it yet. It will be a while before you need multiple inheritance, and a while after that before these points become an issue. Just keep in mind that multiple inheritance is out there, the order of inheritance matters, and before you try anything more complicated than the FieldManager class above, read this article. It explains how the MRO works and why you get that error.
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures


Messages In This Thread
Class Intermediate: Inheritance - by ichabod801 - Sep-15-2016, 11:16 PM
RE: Class Intermediate: Inheritance - by ichabod801 - Sep-15-2016, 11:18 PM
RE: Class Intermediate: Inheritance - by ichabod801 - Sep-15-2016, 11:25 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  [Classes] Class Intermediate: Operator Overloading ichabod801 3 10,374 Sep-15-2016, 11:07 PM
Last Post: ichabod801

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020