Article Directory
Let me summarize the relationship between inheritance, abstract base class and interface in Python in one sentence: the interface mechanism in Python can be realized through abstract base class, and the realization of interface depends on inheritance mechanism.
1. Inheritance
Inheritance is one of the three characteristics of object-oriented programming languages (the other two are encapsulation and polymorphism ). The so-called inheritance means that the subclass automatically has the methods and attributes defined by the parent class, without the need for the subclass to repeatedly define methods with the same name. Or attributes, so one of the biggest advantages of inheritance is that it can improve the degree of code reuse.
1. Common series cases
Here is an introduction to the concept of Python inheritance, an important concept in high school mathematics-sequence of numbers. A sequence is a sequence composed of a set of values. Each value in the sequence depends on the previous item or multiple items in the sequence, for example:
- For an arithmetic sequence, starting from the second item in the sequence, each item is obtained by adding a fixed constant to the previous item;
- For a geometric sequence, starting from the second term in the sequence, each term is obtained by multiplying the previous term by a fixed constant;
- For the Fibonacci sequence, starting from the third item in the sequence, each item has the sum of the first two items.
If we now need to use object-oriented features to abstract the codes of the above-mentioned different types of series, we can imagine that the three series of series must support the following methods with similar functions:
- Initialization method : used to initialize the first few items of the sequence;
- Traversal support method : It can support traversing the items of the sequence in an iterable manner;
- Sequence item generation method : According to certain rules, generate any item according to the first several items.
If the method of inheritance is not adopted, the final realization of each sequence class will have a high degree of code repetition.
2. Common sequence realization
In response to the above discussion, consider the use of inheritance to implement each sequence class:
- First, define a generic sequence parent class
Progression
, in which the common methods and practical methods of the sequence are realized; - Then, the inherited
Progression
class rewrites the parent class method or defines a new method in the arithmetic, equal ratio, and Fibonacci sequence according to the generation rules of the general term of the sequence.
Base class
In the sequence base class:
__init__
The initialization method receives two parametersstart
to specify the value of the first item in the sequence , and to specify the number of sequence itemsnum
to be printed by default, which are used to initialize the_current
sum_num
value respectively;_advance
The method is used to generate any item according to the general rules of the number sequence;__iter__
And__next__
methods are used to support Python's iterator protocol (for details, please refer to the exploration of for loop operation mechanism in Python and detailed explanation of iterable objects and iterators ) for traversal of series;__str__
The method is used to convert the sequence object into a list and return the string representation of the list.
class Progression:
"""数列基类"""
def __init__(self, start=0, num=10):
"""
将当前数列的第一项初始化为0
:param start: 数列第一项,默认为0
:param num: 打印数列时的默认显示项数
"""
self._current = start
self._num = num
def _advance(self):
"""用于根据数列前若干项进行任意项的生成,该方法应该被子类重写"""
self._current += 1
def __next__(self):
"""迭代器协议方法,返回数列中的下一项,当已至数列最后一项则抛出StopIteration异常"""
if self._num > 0:
ans = self._current
self._advance()
self._num -= 1
return ans
else:
raise StopIteration
def __iter__(self):
"""迭代器协议方法,返回对象自身"""
return self
def __str__(self):
"""返回对象的字符串表示形式"""
return str(list(self))
Arithmetic sequence
In the realization of the arithmetic sequence, due to the inherited Progression
class, so:
__init__
In the method, the parent class initialization method is called to initialize_current
the number of sequence items inherited from the parent class and printed by default_num
, and also initialize the arithmetic sequence constants unique to this class_increment
;_advance
The method rewrites the parent method with the same name in accordance with the general rule of arithmetic sequence;__next__
,__iter__
And__str__
methods are inherited from theProgression
class, no need to rewrite the code.
class ArithmeticProgression(Progression):
"""等差数列"""
def __init__(self, start=0, increment=1, num=10):
"""
创建一个新的等差数列
:param increment: 等差常量,默认为1
:param start: 数列首项,默认为0
:param num: 打印数列时的默认显示项数
"""
super().__init__(start=start, num=num)
self._increment = increment
def _advance(self): # 重写父类同名方法
"""根据等差数列通项规则,生成任意项"""
self._current += self._increment
Geometric sequence
In the realization of the geometric sequence, because of the inherited Progression
class, so:
__init__
In the method, the parent class initialization method is called to initialize_current
the number of sequence items inherited from the parent class and printed by default_num
, and also initialize the geometric sequence constants unique to this class_base
;_advance
The method rewrites the method of the same name in the parent class according to the general rules of the geometric sequence;__next__
,__iter__
And__str__
methods are inherited from theProgression
class, no need to rewrite the code.
class GeometricProgression(Progression):
"""等比数列"""
def __init__(self, start=1, num=10, base=2):
"""
创建一个新的等比数列
:param base: 等比常量,默认值为2
:param start: 数列首项,默认为1
:param num: 打印数列时的默认显示项数
"""
super().__init__(start=start, num=num)
self._base = base
def _advance(self):
"""根据等比数列通项规则,生成任意项"""
self._current *= self._base
Fibonacci sequence
In the realization of Fibonacci sequence, because of inheriting the Progression
class, so:
__init__
The parent class initialization method is called in the method to initialize_current
the number of sequence items inherited from the parent class and printed by default_num
, and also initialize the imaginary item 0 unique to the class_prev
;_advance
The method rewrites the method of the same name in the parent class according to the general rules of the Fibonacci sequence;__next__
,__iter__
And__str__
methods are inherited from theProgression
class, no need to rewrite the code.
class FibonacciProgression(Progression):
"""斐波那契数列"""
def __init__(self, first=0, second=1, num=10):
"""
创建一个新的斐波那契数列
:param first: 数列第一项,默认为0
:param second: 数列第二项,默认为1
:param num: 打印数列时的默认显示项数
"""
super().__init__(start=first, num=num)
self._prev = second - first # 假想在第一项之前存在的第零项
def _advance(self):
"""根据斐波那契数列通项规则,生成任意项"""
self._prev, self._current = self._current, self._prev + self._current
The following are the test results of the above several series of realization classes:
if __name__ == '__main__':
print('默认数列Progression:')
print(Progression(num=5), end='\n'*2) # [0, 1, 2, 3, 4]
print('等差数列ArithmeticProgression:')
print(ArithmeticProgression(start=10, increment=3, num=7), end='\n'*2) # [10, 13, 16, 19, 22, 25, 28]
print('等比数列GeometricProgression:')
print(GeometricProgression(start=4, base=3, num=9), end='\n'*2) # [4, 12, 36, 108, 324, 972, 2916, 8748, 26244]
print('斐波那契数列FibonacciProgression:')
print(FibonacciProgression(first=2, num=12)) # [2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123, 199]
Two, abstract base class
Careful analysis of the above code is found, the base class Progression
is intended as only ArithmeticProgression
, GeometricProgression
and the FibonacciProgression
base class, although by instantiating Progression
give an object, but this is of little significance, because it is only ArithmeticProgression
a special case, i.e., the first term is 0, An arithmetic sequence with an arithmetic constant of 1.
In languages that support the object-oriented programming paradigm Progression
, there is a special term for this class that is only used as a base class to specify the methods that multiple subclasses need to implement- abstract base class .
If you want to define an abstract base class in Python3, you can do it through the following steps:
- Before defining the abstract base class ,
abc
import classesABCMeta
and methods from the moduleabstractmethod
; - When defining an abstract base class :
- Specify
metaclass
as after the abstract base class nameABCMeta
; - It is used before the method (generally called abstract method ) that the abstract base class needs to be inherited by subclasses
@abstractmethod
.
- Specify
For example: As mentioned earlier, for the Progression
method, the code to define it as an abstract base class according to the above process is as follows:
from abc import ABCMeta, abstractmethod
class Progression(metaclass=ABCMeta):
"""数列基类"""
def __init__(self, start=0, num=10):
"""
将当前数列的第一项初始化为0
:param start: 数列第一项,默认为0
:param num: 打印数列时的默认显示项数
"""
self._current = start
self._num = num
@abstractmethod
def _advance(self):
"""用于根据数列前若干项进行任意项的生成,该方法应该被子类重写"""
def __next__(self):
"""迭代器协议方法,返回数列中的下一项,当已至数列最后一项则抛出StopIteration异常"""
if self._num > 0:
ans = self._current
self._advance()
self._num -= 1
return ans
else:
raise StopIteration
def __iter__(self):
"""迭代器协议方法,返回对象自身"""
return self
def __str__(self):
"""返回对象的字符串表示形式"""
return str(list(self))
It can be seen that we will _advance
define an abstract method in the above code , because on the one hand, this method must exist in all subclasses, and on the other hand, the implementation of this method in all subclasses is completely different.
It should be noted that, for the abstract base class (eg: Progression
) it can not be directly instantiated by creating an object, otherwise it will report an error like this: TypeError: Can't instantiate abstract class Progression with abstract methods _advance
.
Three, interface
An interface is a programming mechanism that ensures that different code writers can:
- Follow the same code signature, such as: method name (
_advance
), parameters, return value; - Use different algorithms to implement specific codes, such as the
_advance
method of generating rules based on the general term of arithmetic, geometric ratio, and Fibonacci sequence .
The advantage of the interface mechanism is that it can:
- Realize the collaboration between different code writers, such as: application architects can build the overall framework, and leave the specific implementation to the implementation staff. This is a bit like your boss generally tells you to do certain things based on the resources at hand What are the things and expected results, and you and your colleagues need to work hard to implement each thing through a certain process;
- To achieve loose coupling between codes, the implementation personnel of each interface do not need to know the internal specific implementation of the interface by other personnel.
In Python, for the specific implementation of an interface , as long as the subclass inherits the abstract base class , and then implements all the abstract methods in it .
The following is an example of the above sequence class to demonstrate the interface implementation process:
from abc import ABCMeta, abstractmethod
class Progression(metaclass=ABCMeta):
"""数列基类"""
def __init__(self, start=0, num=10):
"""
将当前数列的第一项初始化为0
:param start: 数列第一项,默认为0
:param num: 打印数列时的默认显示项数
"""
self._current = start
self._num = num
@abstractmethod
def _advance(self):
"""用于根据数列前若干项进行任意项的生成,该方法应该被子类重写"""
def __next__(self):
"""迭代器协议方法,返回数列中的下一项,当已至数列最后一项则抛出StopIteration异常"""
if self._num > 0:
ans = self._current
self._advance()
self._num -= 1
return ans
else:
raise StopIteration
def __iter__(self):
"""迭代器协议方法,返回对象自身"""
return self
def __str__(self):
"""返回对象的字符串表示形式"""
return str(list(self))
class FibonacciProgression(Progression):
"""斐波那契数列"""
def __init__(self, first=0, num=10, second=1):
"""
创建一个新的斐波那契数列
:param first: 数列第一项,默认为0
:param second: 数列第二项,默认为1
:param num: 打印数列时的默认显示项数
"""
super().__init__(start=first, num=num)
self._prev = second - first # 假想在第一项之前存在的第零项
def _advance(self):
"""根据斐波那契数列通项规则,生成任意项"""
self._prev, self._current = self._current, self._prev + self._current
if __name__ == '__main__':
print(FibonacciProgression(first=2, num=12)) # [2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123, 199]