• Python学习笔记-实现接口的两种方法


    一、接口

    接口是用来限制应用程序实现函数的类必须按照给定的方法进行调用的函数。
    一般包含一个或多个的抽象方法。
    是面向对象变成中实现多态的一个重要手段。
    接口是虚拟的,不能示例化,需要继承的子类需要实现对应的你方法。
    在这里插入图片描述
    如上,通过接口来限定不同的动物的叫声,这样,不管子类是什么动物,只要调用父类的Crow函数,就可以都实现所有动物的叫喊动作,而不用关具体的动物是什么叫声。

    二、Python实现接口

    Python中并没有interface关键字,无法向Java、C#等直接创建抽象类。但是可以通过定义一个包含抽象方法的类的形式来作为接口。

    2.1 通过定义一个函数抛出异常来实现接口

    接口是抽象的,python可以通过定义一个抛出异常的函数的方法来限制子类需要重写方法,达到类似抽象方法的形式。

    class IAnimal():
        """Animal interface"""
    
        def crow(self):
            # 直接报方法未实现的错误
            raise NotImplementedError
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如上,通过定义一个接口类,然后定义函数,直接抛出未实现的错误,这样就可以让子类需要重写改函数,否则调用函数的时候就会报错。
    注意: 按照这种方法实现的类,只要不调用该方法就不会报错。

    2.2 通过ABC模块定义抽象类实现接口

    python中可以通过abc模块引入抽象定义一个抽象类,这样子类只要不重写抽象方法就会直接报错,而不是没有调用方法就不会报错。

    class IAnimal(metaclass = ABCMeta):	# 注意,此处metaclass可能没有智能提示,直接手动输入即可。
        """Animal interface"""
    
        @abstractmethod # 抽象类标志,此处用于标识方法是抽象方法,需要子类重写,无标志时会当成普通方法,没有错误提示。
        def crow(self):
            pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    三、接口的继承

    在Python中,一个类可以通过继承的形式,来继承接口,同时支持继承多个接口。
    子类需要重写继承的抽象方法,才能保证正常运行。

    class Cat(IAnimal):
        """猫咪类"""
        
        def crow(self):
            
            return "喵...喵"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    四、接口的使用

    子类继承接口,并重写了抽象函数之后,就可以通过实例化子类,然后直接调用抽象函数,即可实现接口。

    c = Cat()
    print(c.crow()) 
    
    • 1
    • 2

    4.1 抛出异常的接口使用示例

    定义了一个接口,直接抛出一个异常,这样子类继承之后,如果不重写接口,会直接报错。

    class IAnimal():
        """Animal interface"""
    
        def crow(self):
            # 直接报方法未实现的错误
            raise NotImplementedError
        
    class Cat(IAnimal):
        """猫咪类"""
    
        def crow(self):
            
            return "喵...喵"
        
    class Dog(IAnimal):
        "小狗类"
        pass
    
    
    c = Cat()
    print(c.crow())    
    
    d = Dog()
    print("实例化dog类")
    
    print(d.crow())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    结果如下:
    小猫有重写了抽象函数,可以正常运行。

    ...# 小猫直接示例化了
    实例化dog类
    
    • 1
    • 2

    小狗继承了接口,但是没有重写抽象函数,可以实例化,但是当调用Dog的crow方法时报错了。
    在这里插入图片描述

    4.2 定义一个抽象类的形式实现接口

    如下使用abc模块创建一个抽象类,继承接口后,如果不重写抽象方法,就无法实例化。

    # _*_ coding:utf-8 _*_ 
    
    from abc import ABCMeta,abstractmethod
    
    class IAnimal(metaclass = ABCMeta):
        """Animal interface"""
    
        @abstractmethod # 抽象类标志
        def crow(self):
            pass
        
    class Cat(IAnimal):
        """猫咪类"""
    
        def crow(self):
            
            return "喵...喵"
        
    class Dog(IAnimal):
        "小狗类"
        pass
    
    
    c = Cat()
    print(c.crow())
    
    d = Dog()
    print("实例化dog类")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    结果:
    cat类有重写抽象方法,正常输出:
    在这里插入图片描述
    dog类未重写抽象方法,报未初始化错误“TypeError: Can’t instantiate abstract class Dog with abstract methods crow”:
    在这里插入图片描述

    五、接口的应用示例

    接口的使用可以规范不同的类按照一定的规则,实现某些特定的方法,这样调用的时候就可以按照接口的方式直接调用相关函数的方法,而不用考虑具体子类具体如何实现。

    5.1 以动物作为示例,基本信息如下:

    • 不同动物具有一些相同的行为或技能,如跑、叫、爬树、游泳等
    • 猫会跑、叫、爬树
    • 狗会跑、叫、游泳
    • 鱼会游泳

    5.2 UML类图:

    使用power designer绘制uml类图如下,示意各个类的接口关系,每一个接口都单独实现:
    在这里插入图片描述

    5.3 创建接口类

    接口的创建按照接口隔离原则 设计。
    即使用单一的接口,避免使用一个总的接口,这样子子类不需要实现它不需要接口。
    如下,对各个接口单一定义,一个接口只实现一个功能。

    class IRun(metaclass = ABCMeta):
        """Run interface"""
    
        @abstractmethod # 抽象类标志
        def run(self) -> str:
            pass
    
    class ICrow(metaclass = ABCMeta):
        """Crow interface"""
    
        @abstractmethod # 抽象类标志
        def crow(self) -> str:
            pass
    
    class IClimb(metaclass = ABCMeta):
        """Climb interface"""
    
        @abstractmethod # 抽象类标志
        def climb(self) -> str:
            pass
    
    class ISwim(metaclass = ABCMeta):
        """Swim interface"""
    
        @abstractmethod # 抽象类标志
        def swim(self) -> str:
            pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    5.4 继承接口并重写接口函数

    定义新的子类,继承需要继承的接口,然后重新接口的抽象函数。
    如下,定义了Cat,Dog,Fish三个类,并分别继承了相应的接口。

    class Cat(IRun, ICrow, IClimb):
        """Cat"""
    
        def run(self):
            """run"""
    
            return "小猫会跑。"
    
        def crow(self):
            """crow"""
    
            return "小猫会叫。"
        
        def climb(self) -> str:
            """climb"""
    
            return "小猫会爬树。"
    
    class Dog(IRun, ICrow, ISwim):
        """Dog"""
    
        def run(self):
            """run"""
    
            return "小狗会跑。"
    
        def crow(self):
            """crow"""
    
            return "小狗会叫。"
        
        def swim(self) -> str:
            """swim"""
    
            return "小狗会游泳。"
    
    class Fish(ISwim):
        """Fish"""
        
        def swim(self) -> str:
            """swim"""
    
            return "小鱼会游泳。"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    5.5 使用接口

    实例化子类,然后调用对应的接口函数。

    cat = Cat()
    dog = Dog()
    fish = Fish()
    
    print("-"*10 + " Cat " + "-"*10)
    print(cat.run())
    print(cat.crow())
    print(cat.climb())
    
    print("-"*10 + " Dog " + "-"*10)
    print(dog.run())
    print(dog.crow())
    print(dog.swim())
    
    print("-"*10 + " Fish " + "-"*10)
    print(fish.swim())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果如下

    ---------- Cat ----------
    小猫会跑。
    小猫会叫。
    小猫会爬树。
    ---------- Dog ----------
    小狗会跑。
    小狗会叫。
    小狗会游泳。
    ---------- Fish ----------
    小鱼会游泳。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    六、总结

    • 使用抽象类方法会更有利于进行限制类的实现,只要没有重写抽象类就会直接报错。

    • 实际实现上都是可以的,只不过抛出异常,有时候被外部捕获,编译的时候无法及时发现,但调试的时候还是可以发现并进行修正的。

    • 使用了接口,相关开发人员就可以不用关很多子类的具体运行方式,可以按照统一的接口进行调用即可。

    • 使用接口可以降低代码间的耦合,提高可扩展性和易用性。

    • 使用接口会增加代码的复杂度。

    • 具体使用过程多用用,就可以根据自己的需求进行使用。

  • 相关阅读:
    解决Mac上执行pip install -e turtle 报错
    http与https的区别
    【m98】RtpSequenceNumberMap:记录RTP序列号与时间戳
    代码随想录 | Day 53 - LeetCode 1143. 最长公共子序列、LeetCode 1035. 不相交的线、LeetCode 53. 最大子序和
    Docker版iServer新手入门教程
    用 DataGridView 控件显示数据
    Redis之Lua脚本讲解
    Python每日一练-DAY01
    计算机毕业设计ssm+vue基于微信的产品订单管理小程序
    javaScript:什么是事件对象?事件原对象?如何获取坐标点?
  • 原文地址:https://blog.csdn.net/u010839204/article/details/134013582