python Is one of the few modern programming languages that supports multiple inheritance . Multiple inheritance is the ability to derive a class from multiple base classes at the same time

Multiple inheritance has a bad reputation , So that most modern programming languages don't support it . contrary , The concept of modern programming language support interface . In these languages , You inherit from a single base class , Then implement multiple interfaces , So your classes can be reused in different situations

This approach brings some limitations to your design . You can only inherit the implementation of a class by directly deriving it . You can implement multiple interfaces , But cannot inherit the implementation of multiple classes

This constraint is good for software design , Because it forces you to reduce dependencies when designing classes . You will see later in this article , You can leverage multiple implementations by combining them , This makes the software more flexible . however , This section is about multiple inheritance , So let's see how it works

Proven facts , Sometimes a temporary secretary is hired when there is too much paperwork to do . Temporary secretaries play the role of secretary in the context of productivity system , But for payroll purposes , It's
HourlyEmployee

Derived from Secretary: You can derive from Secretary, To inherit the .work() method , Then overlay .calculate_payroll() method , Implement it as
HourlyEmployee

from HourlyEmployee derive : You can HourlyEmployee Derive to inherit .calculate_payroll() method , Then rewrite .work()
Method to implement it as a secretary
# In employees.py class TemporarySecretary(Secretary, HourlyEmployee): pass
Python Allows you to inherit from two different classes by specifying them between parentheses in the class declaration

Now? , You modify the program to add new temporary secretary staff
import hr import employees import productivity manager = employees.Manager(1,
'Mary Poppins', 3000) secretary = employees.Secretary(2, 'John Smith', 1500)
sales_guy = employees.SalesPerson(3, 'Kevin Bacon', 1000, 250) factory_worker =
employees.FactoryWorker(4, 'Jane Doe', 40, 15) temporary_secretary =
employees.TemporarySecretary(5, 'Robin Williams', 40, 9) company_employees = [
manager, secretary, sales_guy, factory_worker, temporary_secretary, ]
productivity_system = productivity.ProductivitySystem()
productivity_system.track(company_employees, 40) payroll_system =
hr.PayrollSystem() payroll_system.calculate_payroll(company_employees)
Run program
$ python program.py Traceback (most recent call last): File ".\program.py",
line 9, in <module> temporary_secretary = employee.TemporarySecretary(5, 'Robin
Williams', 40, 9) TypeError: __init__() takes 4 positional arguments but 5 were
given
You will receive a TypeError abnormal , This exception indicates that there should be 4 Location parameters , But given 5 individual

This is because you first derive from the Secretary TemporarySecretary, And then from HourlyEmployee Derived from , So the interpreter tries to use Secretary .__
init __() To initialize an object .

ok , Let's turn around
class TemporarySecretary(HourlyEmployee, Secretary): pass
Run program
$ python program.py Traceback (most recent call last): File ".\program.py",
line 9, in <module> temporary_secretary = employee.TemporarySecretary(5, 'Robin
Williams', 40, 9) File "employee.py", line 16, in __init__ super().__init__(id,
name) TypeError: __init__() missing 1 required positional argument:
'weekly_salary'
Now it seems , You are missing a weekly Secretary parameter , This parameter is required to initialize the director , But in TemporarySecretary The parameter has no meaning in the context of , Because it's
HourlyEmployee

Maybe TemporarySecretary .__ init __() It will help
# In employees.py class TemporarySecretary(HourlyEmployee, Secretary): def
__init__(self, id, name, hours_worked, hour_rate): super().__init__(id, name,
hours_worked, hour_rate)
try it
$ python program.py Traceback (most recent call last): File ".\program.py",
line 9, in <module> temporary_secretary = employee.TemporarySecretary(5, 'Robin
Williams', 40, 9) File "employee.py", line 54, in __init__ super().__init__(id,
name, hours_worked, hour_rate) File "employee.py", line 16, in __init__
super().__init__(id, name) TypeError: __init__() missing 1 required positional
argument: 'weekly_salary'
It doesn't work . okay , Now it's a deep study Python Method parsing order of (MRO) It's time , See what happened

When accessing a method or property of a class ,Python Use class MRO To find it .super() Also use MRO To determine which method or property to call . You can use the Python super() stay
Supercharge Learn about super() More about
from employees import TemporarySecretary TemporarySecretary.__mro__ (<class
'employees.TemporarySecretary'>, <class 'employees.HourlyEmployee'>, <class
'employees.Secretary'>, <class 'employees.SalaryEmployee'>, <class
'employees.Employee'>, <class 'object'> )
MRO display Python Order to find matching properties or methods . In the example , This is what we created TemporarySecretary What happens when an object

*
call TemporarySecretary .__ init __(self,id,name,hours_worked,hour_rate) method

*
super().__ init __(id,name,hours_worked,hour_rate) Call and HourlyEmployee .__ init
__(self,id,name,hour_worked,hour_rate)

*
HourlyEmployee call super().__ init __(id,name),MRO Will match Secretary . secretary .__ init __(), It's inherited from
SalaryEmployee .__ init __(self,id,name,weekly_salary)

Due to parameter mismatch , Therefore, the TypeError abnormal

You can reverse the inheritance order and call directly HourlyEmployee .__ init __() To bypass MRO, As follows
class TemporarySecretary(Secretary, HourlyEmployee): def __init__(self, id,
name, hours_worked, hour_rate): HourlyEmployee.__init__(self, id, name,
hours_worked, hour_rate)
This solves the problem of creating objects , But there are similar problems when trying to calculate salaries . You can run the program to view problems
$ python program.py Tracking Employee Productivity
============================== Mary Poppins screams and yells for 40 hours.
John Smith expends 40 hours doing office paperwork. Kevin Bacon expends 40
hours on the phone. Jane Doe manufactures gadgets for 40 hours. Robin Williams
expends 40 hours doing office paperwork. Calculating Payroll
=================== Payroll for: 1 - Mary Poppins - Check amount: 3000 Payroll
for: 2 - John Smith - Check amount: 1500 Payroll for: 3 - Kevin Bacon - Check
amount: 1250 Payroll for: 4 - Jane Doe - Check amount: 600 Payroll for: 5 -
Robin Williams Traceback (most recent call last): File ".\program.py", line 20,
in <module> payroll_system.calculate_payroll(employees) File "hr.py", line 7,
in calculate_payroll print(f'- Check amount: {employee.calculate_payroll()}')
File "employee.py", line 12, in calculate_payroll return self.weekly_salary
AttributeError: 'TemporarySecretary' object has no attribute 'weekly_salary'
Now the problem is , Because you reversed the order of inheritance ,MRO Will be in HourlyEmployee In SalariedEmployee Found before method SalariedEmployee Of
.calculate_payroll() method . You need to TemporarySecretary Medium coverage .calculate_payroll() And invokes the correct implementation.
class TemporarySecretary(Secretary, HourlyEmployee): def __init__(self, id,
name, hours_worked, hour_rate): HourlyEmployee.__init__(self, id, name,
hours_worked, hour_rate) def calculate_payroll(self): return
HourlyEmployee.calculate_payroll(self)
compute_payroll() Method direct call HourlyEmployee.calculate_payroll()
To ensure the right results . You can run the program again to see if it works
$ python program.py Tracking Employee Productivity
============================== Mary Poppins screams and yells for 40 hours.
John Smith expends 40 hours doing office paperwork. Kevin Bacon expends 40
hours on the phone. Jane Doe manufactures gadgets for 40 hours. Robin Williams
expends 40 hours doing office paperwork. Calculating Payroll
=================== Payroll for: 1 - Mary Poppins - Check amount: 3000 Payroll
for: 2 - John Smith - Check amount: 1500 Payroll for: 3 - Kevin Bacon - Check
amount: 1250 Payroll for: 4 - Jane Doe - Check amount: 600 Payroll for: 5 -
Robin Williams - Check amount: 360
The program works now , Because you can force the method resolution order by explicitly telling the interpreter which method we want to use .

As you can see , Multiple inheritance can be confusing , Especially when you meet diamond When in question

This figure shows the diamond problem .TemporarySecretary Derived from two classes using multiple inheritance , These two classes also derive from Employee
. This will cause two paths to arrive Employee Base class , This is what you want to avoid in your design

When you use multiple inheritance and derive from two classes with a common base class ,diamond Problems will arise . This may result in the wrong version of the calling method

As you can see ,Python Provides a method to force the correct method to be called , And analyze MRO Can help you understand the problem

Employee Derived classes are used by two different systems

*
Productivity system to track employee productivity

*
Salary system for calculating employee salary

This means that everything about productivity should be in one module , And everything about pay should be in another module . You can start changing the productivity module
# In productivity.py class ProductivitySystem: def track(self, employees,
hours): print('Tracking Employee Productivity')
print('==============================') for employee in employees: result =
employee.work(hours) print(f'{employee.name}: {result}') print('') class
ManagerRole: def work(self, hours): return f'screams and yells for {hours}
hours.' class SecretaryRole: def work(self, hours): return f'expends {hours}
hours doing office paperwork.' class SalesRole: def work(self, hours): return
f'expends {hours} hours on the phone.' class FactoryRole: def work(self,
hours): return f'manufactures gadgets for {hours} hours.'
Productivity module implementation ProductivitySystem Classes and related roles they support . These classes implement the work() Interface , But they're not from Employee Derived
# In hr.py class PayrollSystem: def calculate_payroll(self, employees):
print('Calculating Payroll') print('===================') for employee in
employees: print(f'Payroll for: {employee.id} - {employee.name}') print(f'-
Check amount: {employee.calculate_payroll()}') print('') class SalaryPolicy:
def __init__(self, weekly_salary): self.weekly_salary = weekly_salary def
calculate_payroll(self): return self.weekly_salary class HourlyPolicy: def
__init__(self, hours_worked, hour_rate): self.hours_worked = hours_worked
self.hour_rate = hour_rate def calculate_payroll(self): return
self.hours_worked * self.hour_rate class CommissionPolicy(SalaryPolicy): def
__init__(self, weekly_salary, commission): super().__init__(weekly_salary)
self.commission = commission def calculate_payroll(self): fixed =
super().calculate_payroll() return fixed + self.commission
hr The module realizes PayrollSystem, The system calculates wages for employees . It also implements the policy class of payroll . As you can see , Policy category no longer derived from Employee
# In employees.py from hr import ( SalaryPolicy, CommissionPolicy,
HourlyPolicy ) from productivity import ( ManagerRole, SecretaryRole,
SalesRole, FactoryRole ) class Employee: def __init__(self, id, name): self.id
= id self.name = name class Manager(Employee, ManagerRole, SalaryPolicy): def
__init__(self, id, name, weekly_salary): SalaryPolicy.__init__(self,
weekly_salary) super().__init__(id, name) class Secretary(Employee,
SecretaryRole, SalaryPolicy): def __init__(self, id, name, weekly_salary):
SalaryPolicy.__init__(self, weekly_salary) super().__init__(id, name) class
SalesPerson(Employee, SalesRole, CommissionPolicy): def __init__(self, id,
name, weekly_salary, commission): CommissionPolicy.__init__(self,
weekly_salary, commission) super().__init__(id, name) class
FactoryWorker(Employee, FactoryRole, HourlyPolicy): def __init__(self, id,
name, hours_worked, hour_rate): HourlyPolicy.__init__(self, hours_worked,
hour_rate) super().__init__(id, name) class TemporarySecretary(Employee,
SecretaryRole, HourlyPolicy): def __init__(self, id, name, hours_worked,
hour_rate): HourlyPolicy.__init__(self, hours_worked, hour_rate)
super().__init__(id, name)
employees Modules import policies and roles from other modules , And realize different Employee type . You still use multiple inheritance to inherit salary Strategy class and productivity
Role realization , But the implementation of each class only needs to deal with initialization

be careful , You still need to explicitly initialize the salary policy in the constructor . You may see Manager and Secretary The initialization of is the same . in addition ,factory - worker and
TemporarySecretary The initialization of is the same

You won't want to use this code duplication in more complex designs , So you have to be careful when designing class hierarchies

Run program
$ python program.py Tracking Employee Productivity
============================== Mary Poppins: screams and yells for 40 hours.
John Smith: expends 40 hours doing office paperwork. Kevin Bacon: expends 40
hours on the phone. Jane Doe: manufactures gadgets for 40 hours. Robin
Williams: expends 40 hours doing office paperwork. Calculating Payroll
=================== Payroll for: 1 - Mary Poppins - Check amount: 3000 Payroll
for: 2 - John Smith - Check amount: 1500 Payroll for: 3 - Kevin Bacon - Check
amount: 1250 Payroll for: 4 - Jane Doe - Check amount: 600 Payroll for: 5 -
Robin Williams - Check amount: 360

Technology
©2019-2020 Toolsou All rights reserved,
TP6 Application examples of verifier and correct verification data Unity Scene loading asynchronously ( Implementation of loading interface ) Gude Haowen serial - You deserve to be an engineer ( Preface ) Bitcoin in ten years ,VDS Opportunity or fraud Huawei certification HCIA-AI artificial intelligence Image explanation of over fitting and under fitting Python Basic knowledge and notes ESP8266/ESP32 System : Optimize system startup time First knowledge python Skills summary use C++ I want to talk to you “ Prototype mode ” ( copy / copy constructor )