|
1 | | -# About |
2 | | - |
3 | | -Inheritance is one of the ['four pillars'][four-pillars] of Object Oriented Programming (`OOP`). |
4 | | -In situations where only a small amount of functionality needs to be customized for a new class, `inheritance` allows code re-use from one or more parent classes, and can help make programs cleaner and more maintainable. |
5 | | - |
6 | | -## Inheritance |
7 | | - |
8 | | -`Inheritance` describes `is a kind of` relationship between two or more classes, abstracting common details into super (_base_ or _parent_) class and storing specific ones in the subclass (_derived class_ or _child class_). |
9 | | - |
10 | | -To create a child class, specify the parent class name inside the pair of parenthesis, followed by its name. |
11 | | -Example |
12 | | -```python |
13 | | -class Child(Parent): |
14 | | - pass |
15 | | -``` |
16 | | -Every child class inherits all the behaviors (_attributes, constructors, methods_) exhibited by their parent class. |
17 | | - |
18 | | - |
19 | | -## Single Inheritance |
20 | | - |
21 | | -When a derived (or child) class inherits only from one base (or parent) class, it is known as _single inheritance_. |
22 | | - |
23 | | - |
24 | | -```python |
25 | | -# The parent or base class. |
26 | | -class Person: |
27 | | - |
28 | | - def __init__(self, fname, lname): |
29 | | - self.fname = fname |
30 | | - self.lname = lname |
31 | | - |
32 | | -# The child or derived class, inheriting from Person. |
33 | | -class Employee(Person): |
34 | | - |
35 | | - all_employees = [] |
36 | | - def __init__(self, fname, lname, empid): |
37 | | - # Using the Parent constructor to create the base object. |
38 | | - Person.__init__(self, fname, lname) |
39 | | - |
40 | | - # Adding an attribute specific to the Child class. |
41 | | - self.empid = empid |
42 | | - |
43 | | - Employee.all_employees.append(self) |
44 | | -``` |
45 | | -`Employee` class is derived from `Person`. |
46 | | -Now, we can create an `Employee` object. |
47 | | - |
48 | | - |
49 | | -```python |
50 | | -... |
51 | | -p1 = Person('George', 'smith') |
52 | | -print(p1.fname, '-', p1.lname) |
53 | | -e1 = Employee('Jack', 'simmons', 456342) |
54 | | -e2 = Employee('John', 'williams', 123656) |
55 | | -print(e1.fname, '-', e1.empid) |
56 | | -print(e2.fname, '-', e2.empid) |
57 | | -``` |
58 | | -After running the program we will get the following output |
59 | | -```bash |
60 | | - |
61 | | -George - smith |
62 | | -Jack - 456342 |
63 | | -John - 123656 |
64 | | -``` |
65 | | -## Multiple Inheritance |
66 | | -As we've seen, `single inheritance` is where a class inherits directly from another class. |
67 | | -On the other side, `multiple inheritance` is a Python feature that allows a child class to inherit characteristics and methods from more than one parent class. |
68 | | - |
69 | | -```python |
70 | | -class SubclassName(BaseClass1, BaseClass2, ...): |
71 | | - pass |
72 | | -``` |
73 | | -### Multiple Inheritance and the Diamond Problem |
74 | | - |
75 | | -The "diamond problem" (also known as the "deadly diamond of death") refers to an ambiguity that occurs when two classes B and C inherit from a superclass A, while another class D inherits from both B and C. If A has a method "m" that B or C (or even both of them) has overridden, and if it does not override this method, the question becomes which version of the method D inherits. It's possible that it's from A, B, or C. |
76 | | -Let's have a look at the problem using an example: |
77 | | - |
78 | | -```python |
79 | | -class A: |
80 | | - def m(self): |
81 | | - print("m of A called") |
82 | | -class B(A): |
83 | | - def m(self): |
84 | | - print("m of B called") |
85 | | -class C(A): |
86 | | - def m(self): |
87 | | - print("m of C called") |
88 | | -class D(B,C): |
89 | | - pass |
90 | | -``` |
91 | | -If we call an instance x of class D, we will get the output as `m of B called`. But if we interchange the order of inheritance in class D i.e. `Class D(C, D)`. We will get the output as `m of C called`. |
92 | | -To solve the diamond problem in python, we will look into a new method `mro()`. |
93 | | -### Method resolution order(MRO) |
94 | | - |
95 | | -To get the method resolution order of a class we can use either `__mro__` attribute or `mro()` method. By using these methods we can display the order in which methods are resolved. For Example |
96 | | - |
97 | | -```python |
98 | | -class A: |
99 | | - def m(self): |
100 | | - print(" m of A called") |
101 | | -class B: |
102 | | - def m(self): |
103 | | - print(" m of B called") |
104 | | - |
105 | | -# classes ordering |
106 | | -class C(A, B): |
107 | | - def __init__(self): |
108 | | - print("Constructor C") |
109 | | - |
110 | | -r = C() |
111 | | - |
112 | | -# it prints the lookup order |
113 | | -print(C.__mro__) |
114 | | -print(C.mro()) |
115 | | -``` |
116 | | -The output |
117 | | -```cmd |
118 | | -Constructor C |
119 | | -(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>) |
120 | | -[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] |
121 | | -``` |
122 | | -### Mixins |
123 | | -A mixin is a type of multiple inheritance that is unique. Mixins are typically employed in one of two scenarios: |
124 | | - |
125 | | -1. We wish to give a class a number of optional features. |
126 | | -1. We want to use a specific feature in a variety of classes. |
127 | | - |
128 | | -For example |
129 | | -```python |
130 | | -class A1(object): |
131 | | - def method(self): |
132 | | - return 1 |
133 | | - |
134 | | -class A2(object): |
135 | | - def method(self): |
136 | | - return 2 |
137 | | - |
138 | | -class B1(object): |
139 | | - def usesMethod(self): |
140 | | - return self.method() + 10 |
141 | | - |
142 | | -class B2(object): |
143 | | - def usesMethod(self): |
144 | | - return self.method() + 20 |
145 | | - |
146 | | -class C1_10(A1, B1): pass |
147 | | -class C1_20(A1, B2): pass |
148 | | -class C2_10(A2, B1): pass |
149 | | -class C2_20(A2, B2): pass |
150 | | -``` |
151 | | -Mixins helps us to recombine functionalities with different choices of base classes. |
152 | | -#### Pros and Cons of Mixins |
153 | | -| Advantages | Disadvantages | |
154 | | -|:-- | :-- | |
155 | | -|Mixin classes tend to be simple because they represent simple orthogonal concepts. | Execution of statements at run time tends to jump around in different mixins, making it hard to follow and debug| |
156 | | -|Helps us to recombine functionalities with different choices | Potential for long compile times| |
157 | | -## __super()__ |
158 | | -In a nutshell, `super()` gives us access to methods in a superclass from the subclass that inherits from it. |
159 | | -`super()` by itself returns a temporary object of the superclass, which may subsequently be used to call the methods of that superclass. |
160 | | - |
161 | | -But why we want to use `super()`? |
162 | | - |
163 | | -Using `super()` to call already created methods avoids having to rebuild those methods in our subclass and allows us to swap out superclasses with little code modifications. |
164 | | - |
165 | | -[four-pillars]: https://www.educative.io/edpresso/what-are-the-four-pillars-of-oops-in-python |
166 | | - |
167 | | -[four-pillars]: https://www.educative.io/edpresso/what-are-the-four-pillars-of-oops-in-python |
168 | | - |
| 1 | +# TODO: Add about for this concept. |
0 commit comments