Inheritance, abstract base classes and interfaces in Python

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 Progressionclass 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 parameters startto specify the value of the first item in the sequence , and to specify the number of sequence items numto be printed by default, which are used to initialize the _currentsum _numvalue respectively;
  • _advanceThe 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 Progressionclass, so:

  • __init__In the method, the parent class initialization method is called to initialize _currentthe 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;
  • _advanceThe 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 the Progressionclass, 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 Progressionclass, so:

  • __init__In the method, the parent class initialization method is called to initialize _currentthe 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;
  • _advanceThe 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 the Progressionclass, 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 Progressionclass, so:

  • __init__The parent class initialization method is called in the method to initialize _currentthe 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;
  • _advanceThe 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 the Progressionclass, 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 Progressionis intended as only ArithmeticProgression, GeometricProgressionand the FibonacciProgressionbase class, although by instantiating Progressiongive an object, but this is of little significance, because it is only ArithmeticProgressiona 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 , abcimport classes ABCMetaand methods from the module abstractmethod;
  • When defining an abstract base class :
    • Specify metaclassas after the abstract base class name ABCMeta;
    • It is used before the method (generally called abstract method ) that the abstract base class needs to be inherited by subclasses @abstractmethod.

For example: As mentioned earlier, for the Progressionmethod, 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 _advancedefine 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 _advancemethod 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]

Guess you like

Origin blog.csdn.net/weixin_37780776/article/details/108372236