Inheritance
Single inheritance
class Vehicle(object):
    """Document String: Define a Vehicle class"""
 
    def __init__(self, brand, year):
        self._brand = brand
        self._year = year
 
    @property
    def brand(self):
        return self._brand
 
    @brand.setter
    def brand(self, b):
        self._brand = b
 
    @brand.deleter
    def brand(self):
        del self._brand
 
    @property
    def year(self):
        return self._year
 
    @year.setter
    def year(self, y):
        self._year = y
 
    @year.deleter
    def year(self):
        del self._year
 
    def __str__(self):
        return self._brand+' '+str(self._year)
 
    def __getattr__(self, attr): # intercept that inexistent attribute
        return '~'
		
from Vehicle import Vehicle
 
class Car(Vehicle):
    """Define class Car, which is inherit from class Vehicle"""
    def __init__(self, brand, year, model):
        """Construct"""
        print("Create Car Object ...")
        super().__init__(brand, year)
        #super(Car, self).__init__(brand, year)
        #Vehicle.__init__(self, brand, year)
        self.__model = model

    @property
    def model(self):
        return self.__model
 
    @model.setter
    def model(self, value):
        self.__model = value
 
    @model.deleter
    def model(self):
        del self.__model
 
    # override __str__ in Vehicle
    def __str__(self):
        #return super(Car, self).__str__() + " " + self.model
        return Vehicle.__str__(self) + " " + self.model
 
def main():
    v = Vehicle("Buick", 1998)
    print(v)
 
    c = Car("Lincoln", 1998, "Continental");
    print(c)
 
    print(issubclass(Car, Vehicle)) # True
    print(isinstance(c, Car)) # True
 
if __name__ == '__main__':
    main();
		
Abstract class
class Vehicle(object):
    """Abstract base class Vehicle"""
 
    def __init__(self, brand, year):
        if self.__class__ == Vehicle:
            raise NotImplementedError('Cannot create Vehicle instance ...')
 
        self._brand = brand;
        self._year = year;
 
    @property
    def brand(self):
        return self._brand
 
    @brand.setter
    def brand(self, b):
        self._brand = b;
 
    @brand.deleter
    def brand(self):
        del self._brand
 
    @property
    def year(self):
        return self._year
 
    @year.setter
    def year(self, y):
        self._year = y
 
    @year.deleter
    def year(self):
        del self._year
 
    def __str__(self):
        return self._brand+' '+str(self._year)
 
    def __getattr__(self, attr): # intercept that inexistent attribute
        return '~'
		
from Vehicle import Vehicle;
 
class Car(Vehicle):
    """Define class Car, which is inherit from class Vehicle"""
    def __init__(self, brand, year, model):
        """Construct"""
        print("Create Car Object ...") 
        super(Car, self).__init__(brand, year);
	#Vehicle.__init__(self, brand, year);
        self.__model = model;
 
    @property
    def model(self):
        return self.__model
 
    @model.setter
    def model(self, value):
        self.__model = value
 
    @model.deleter
    def model(self):
        del self.__model
 
    # override __str__ in Vehicle
    def __str__(self):
        #return super(Car, self).__str__() + " " + self.model
        return Vehicle.__str__(self) + " " + self.model
 
def main():
    try:
        v = Vehicle("Buick", 1998) # Vehicle is an abstract class, can not be used to create instance
    except Exception as err:
        print(err)
 
    c = Car("Lincoln", 1998, "Continental");
    print(c)
 
if __name__ == '__main__':
    main();
		
Multiple inheritance

  • A given attribute is first searched in the current class. If it’s not found then it’s searched in the parent classes
  • The parent classes are searched in a left-right fashion and each class is searched once
  • class A(object):
        def __init__(self, v):
            self.value = v
        
        def getA(self):
            return 'A'
        
        def __str__(self):
            return 'A: '+str(self.value)
            
    class B(object):
        def __init__(self, n):
            self.name = n
        
        def getB(self):
            return 'B'
        
        def __str__(self):
            return 'B: '+str(self.name)
            
    class C(A, B):
        def __init__(self, v, n):
            A.__init__(self, v)
            B.__init__(self, n)
        
        def getC(self):
            return 'C'
        
        def __str__(self):
            return 'C: '+A.__str__(self)+' '+B.__str__(self)
            
    c = C(43, 'Lin')
    
    print(c.getA()) # A, C inherits getA() from A
    print(c.getB()) # B, C inherits getB() from B
    print(c.getC()) # C, call getC() in C
    
    print(c) # C: A: 43 B: Lin, call __str__() in C, override parent __str__()
    print(C.__str__(c)) # C: A: 43 B: Lin
    
    print(A.__str__(c)) # A: 43, call __str__() in A
    print(B.__str__(c)) # B: Lin, call __str__() in B
            
    Reference
  • Multiple Inheritance in Python
  • Python How to Program, Chapter 9