Mutli-inheritance bug between python 2.7 and 3.7

  sonic0002        2019-05-16 08:33:52       2,826        1    

When we have run a py3.7 code under py2.x or a py2.x code under 3.x ,if a class do mutli-inheritance from 2 parent and the 2 parents also got inheritance from same parent, the different way to handle inheritance chain between python 2 and 3 will cause some running time bug:

Assume we got this sample program inheritTest.py :

# Purpose:     Test mutli-inheritance different between python2 and 3
# Author:      Yuancheng Liu
# Created:     2019/05/16
class A:
    def getVal(self):
        print("call A's getVal()")
    def setVal(self):
        print("call A's setVal()")

class B(A):
    def getVal(self):
        print("call B's getVal()")
    #def setVal(self):
    #    print("call B's setVal()")

class C(A):
    def getVal(self):
        print("call C's getVal()")
    def setVal(self):
        print("call C's setVal()")

class D(B, C):
    pass


import platform
print(platform.python_version())
obj = D()
obj.getVal()

The inheritance chain is like this:

No alt text provided for this image

If we run the program under python 2.7 the result is this:

Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:19:30) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> 
====== RESTART: C:\Singtel\Programs\IOT\IOT\IOT\firmwSign\inhertTest.py ======
2.7.14
call B's getVal()
call A's setVal()
>>> 

But if we run the program under python3.7, the result shows:

PS C:\Singtel\Programs\IOT\IOT\IOT> & C:/Users/dcslyc/AppData/Local/Programs/Python/Python37-32/python.exe c:/Singtel/Programs/IOT/IOT/IOT/firmwSign/inhertTest.py
3.7.3
call B's getVal()
call C's setVal()
call C's setVal()

The inheritance trace of python 2 and python 3 is:

No alt text provided for this image

The reason caused this is the different MRO algorithm between python 2 and three.

In python 2 when we do Mutli-inheritance[class D (B, C)], the inheritance trace is from left to right: B =>A=> C=>A to look for the method.

In python 3 when we do Mutli-inheritance[class D (B, C)], the inheritance trace is: get the header of the first direct inheritance then go through all the parent class to find the shortest sequence in the merged relation list:

inh[D(B ,C)] = D + merge[inh(B(A)) +inh(C(A))]

inh[D(B ,C)] = D + merge[B+merge(A(object)) +C+ merge(A(object)))]

inh[D(B ,C)] = D + merge[B + A=>object + C+ A=>object] O for object class

inh[D(B ,C)] = D + merge[B + AO + C+ AO] O for object class

inh[D(B ,C)] = D + [BAO + BC+ CAO] O for object class # use B as header to trace the shortest set then get BC (contents B and shortest in the list)

inh[D(B ,C)] = D + BC + [BAO + CAO]

So the C's setValue() method is called. So the program get different result when running under python 2 and 3. 

Note: This post is authorized to republish here by Yuancheng Liu, Systems Software Engineer at ZycraftUSV.PTE.LTD. Original post is here.

PYTHON  PYTHON 3  INHERITANCE 

       

  RELATED


  1 COMMENT


Anonymous [Reply]@ 2019-05-16 09:24:00

Just inherit from object on Python 2 and you'll get the same behaviour. Next time read the documentation



  RANDOM FUN

Consequence of not doing functional test