"""Inheritance @see: https://docs.python.org/3/tutorial/classes.html#inheritance Inheritance is one of the principles of object-oriented programming. Since classes may share a lot of the same code, inheritance allows a derived class to reuse the same code and modify accordingly """ # pylint: disable=too-few-public-methods class Person: """Example of the base class""" def __init__(self, name): self.name = name def get_name(self): """Get person name""" return self.name # The syntax for a derived class definition looks like this. # pylint: disable=too-few-public-methods class Employee(Person): """Example of the derived class The Base Class (in our case Person) must be defined in a scope containing the derived class definition. In place of a base class name, other arbitrary expressions are also allowed. Derived classes may override methods of their base classes. Because methods have no special privileges when calling other methods of the same object, a method of a base class that calls another method defined in the same base class may end up calling a method of a derived class that overrides it. An overriding method in a derived class may in fact want to extend rather than simply replace the base class method of the same name. There is a simple way to call the base class method directly: just call BaseClassName.methodname(self, arguments). This is occasionally useful to clients as well. (Note that this only works if the base class is accessible as BaseClassName in the global scope.) """ def __init__(self, name, staff_id): Person.__init__(self, name) # You may also use super() here in order to avoid explicit using of parent class name: # >>> super().__init__(name) self.staff_id = staff_id def get_full_id(self): """Get full employee id""" return self.get_name() + ', ' + self.staff_id def test_inheritance(): """Inheritance.""" # There’s nothing special about instantiation of derived classes: DerivedClassName() creates a # new instance of the class. Method references are resolved as follows: the corresponding class # attribute is searched, descending down the chain of base classes if necessary, and the method # reference is valid if this yields a function object. person = Person('Bill') employee = Employee('John', 'A23') assert person.get_name() == 'Bill' assert employee.get_name() == 'John' assert employee.get_full_id() == 'John, A23' # Python has two built-in functions that work with inheritance: # # - Use isinstance() to check an instance’s type: isinstance(obj, int) will be True only if # obj.__class__ is int or some class derived from int. # # - Use issubclass() to check class inheritance: issubclass(bool, int) is True since bool is # a subclass of int. However, issubclass(float, int) is False since float is not a subclass # of int. assert isinstance(employee, Employee) assert not isinstance(person, Employee) assert isinstance(person, Person) assert isinstance(employee, Person) assert issubclass(Employee, Person) assert not issubclass(Person, Employee)