编程语言
首页 > 编程语言> > Python中的继承、抽象基类和接口

Python中的继承、抽象基类和接口

作者:互联网

先一句话总结Python中继承、抽象基类和接口三者之间的关系:Python中的接口机制可通过抽象基类实现,接口的实现有赖于继承机制。

一、继承

继承是面向对象编程语言的三大特性之一(其他两个是封装、多态),所谓继承是指子类自动具有父类所定义的方法和属性,而无需子类再重复定义同名的方法或属性,因此继承的最大优势之一是可以提高代码的复用程度。

1. 常见数列案例

这里以高中数学中一个重要的概念——数列来简介Python的继承概念。数列是一组数值组成的序列,该序列中的每一个值都取决于数列的前一项或多项,例如:

如果现在需要使用面向对象特性对上述各个不同类型的数列进行代码抽象,则可以想到三个数列类必然都支持下列类似功能的方法:

如果不采用继承的方式,则最终实现的各数列类必然代码重复度很高。

2. 常见数列实现

针对上述讨论,下面考虑使用继承实现各个数列类:

数列基类

在数列基类中:

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))

等差数列

在等差数列的实现中,由于继承了Progression类,所以:

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

等比数列

在等比数列的实现中,由于继承了Progression类,所以:

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

斐波那契数列

在斐波那契数列的实现中,由于继承了Progression类,所以:

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

下面是对上述几个数列实现类的测试结果:

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]

二、抽象基类

仔细分析上述代码可知,基类Progression仅是为了作为ArithmeticProgressionGeometricProgression以及FibonacciProgression的基类,虽然可以通过实例化Progression得到一个对象,但这意义不大,因为其仅是ArithmeticProgression的一种特殊情况,即第一项为0,等差常量为1的等差数列。

在支持面向对象编程范式的语言中,对Progression这种仅作为基类用于指定多个子类所需实现的方法的类,有一个专门的术语——抽象基类。

在Python3中想要定义一个抽象基类,可通过如下步骤实现:

例如:如前所述,对于Progression方法,按照上述流程将其定义为抽象基类的代码如下:

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))

可以看出,上述代码中我们将_advance定义成了抽象方法,因为该方法一方面在所有子类中都必须存在,另一方面该方法在所有子类中的实现又都完全不同。

需要指出的是,对于抽象基类(如:Progression)不能直接对其通过实例化创建对象,否则会报这样的错误:TypeError: Can't instantiate abstract class Progression with abstract methods _advance

三、接口

接口是一种编程机制,这种机制可以确保不同的代码编写者可以:

接口机制的好处在于,可以:

Python中,对于接口的具体实现,只要在子类中继承抽象基类,然后实现其中的所有抽象方法即可。

下面还是以上述的数列类为例演示接口实现的过程:

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, 1



本文首发于python黑洞网,博客园同步更新

标签:__,数列,Progression,Python,self,._,接口,num,基类
来源: https://www.cnblogs.com/pythonzhilian/p/13677084.html