面向对象之多态性(接口类)和抽象类

TOC

多态性

一、什么是多态性

子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写,我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。

多态指的是同一类型的事物,不同的形态,比如动物有很多种形态,猫、狗、猪

class Animal:  # 同一类事物:动物
    def talk(self):
        pass


class Cat(Animal):  # 猫
    def talk(self):
        print('喵喵喵')


class Dog(Animal):  # 狗
    def talk(self):
        print('汪汪汪')


class Pig(Animal):  # 猪
    def talk(self):
        print('哼哼哼')


# 实例化得到三个对象
cat = Cat()
dog = Dog()
pig = Pig()


# 定义统一接口使用,用户无需考虑使用的是什么类
def talk(animal):
    animal.talk()


talk(cat)
talk(dog)
talk(pig)

由于python是动态语言,所以传递给函数talk(animal)的参数animal不一定是父类或者子类型。任何数据类型的实例都可以,只要他有一个talk()的方法即可:

class Duck(animal):
    def talk(self):
        print("嘎嘎嘎")

duck = Duck()  # 再实例化一个对象

def talk(animal):
    animal.talk()

talk(duck)

这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型(不管你是鸭子类,狗类,猫类),只要方法(类中有talk()的方法)在,参数正确,就可以调用。

二、多态的表现

多态的表现“形式之一”就是继承:

  • 先抽象,再继承

父类:定制一套统一的规范。(比如:方法名talk(self)统一)

子类:遵循父类统一的规范。(比如:子类遵循父类方法名的统一)

注意:在python中不会强制限制子类必须遵循父类的规范,所以出现了抽象类

三、多态性的好处

增强了程序的灵活性和可扩展性,我们只需要在继承父类的基础上,再新建一个子类,就可以使用相同的方式去调用obj.talk()

比如上面新建的鸭子类,我们只需要再继承了Animal类(父类)新建一个鸭子类,那么就可以使用相同的方法来多得到一个鸭子叫。

四、多态的目的

是为了在不知道对象具体类型的情况下,统一对象调用方法的规范

比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
def talk(animal):
    animal.talk()

talk(duck)
talk(dog)
talk(cat)

五、从java中的接口类来理解多态性

在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。

继承有两种用途:

  • 继承基类的方法,并且做出自己的改变或者扩展(代码重用)
  • 声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能.
    实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
    
    继承的第二种含义非常重要。它又叫“接口继承”。
    
    
    接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
    

1.做出良好的抽象类,2.规定兼容接口 3.调用者可以无需关心具体实现细节,可以一视同仁处理实现特定接口的所有对象。

#做出一个良好的抽象
class Payment(object):
    #规定了一个兼容接口
    def pay(self):
        pass

#微信支付
class WeChatPay(object):
    def pay(self,money):
        print('微信支付了%s'%money)

#支付宝支付
class AliPay(object):
    def pay(self,money):
        print('支付宝支付了%s'%money)

#苹果支付
class ApplePay(object):
    def pay(self,money):
        print('苹果支付了%s'%money)




def pay(obj,money):
    obj.pay(money)

weixin = WeChatPay()
alipay = AliPay()
applepay = ApplePay()

#调用者无需关心具体实现细节,可以一视同仁的处理实现了特定接口的所有对象
pay(weixin,100)
pay(alipay,200)
pay(applepay,300)
     归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。

依赖倒置原则:

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程

抽象类

一、什么是抽象类

在python内置的abc模块中,有一个抽象类。

二、抽象类的作用

让子类必须遵循父类的编写规范

三、 如何实现抽象类

  • 在父类需要继承abc模块中,metaclass=abc.ABCMeta
  • 在父类的方法中,需要装饰上abc.abstractmethod

注意:在Python中不推荐使用抽象类

注意:子类必须按照父类的方法编写规范,缺一不可。(只要父类中有几个抽象方法,子类就必须要定义几个)

import abc

# 父类
from abc import ABC


class Animal(metaclass=abc.ABCMeta):
    # 方法 吃
    @abc.abstractmethod
    def eat(self):
        pass

    # 方法 叫
    @abc.abstractmethod
    def speak(self):
        pass


# 猪类
class Pig(Animal):
    def run(self):
        pass

    def eat(self):
        print('吃')

    def speak(self):
        print('哼哼')


pig_obj = Pig()
pig_obj.eat()

四、从java中的抽象接口来理解抽象类

1、什么是抽象类

与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

2、为什么要有抽象类

如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类是从一堆中抽取相同的内容而来的,内容包括数据属性和函数属性。

  比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。

从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。

  从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案

在python中实现抽象类

#一切皆文件
import abc #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass

    @abc.abstractmethod #定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #报错,子类没有定义抽象方法

class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)

3、抽象类和抽象接口类

     *抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。*

     ***抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计*** 

     在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。

3.1 多继承问题

    在继承抽象类的过程中,我们应该尽量避免多继承;

而在继承接口的时候,我们反而鼓励你来多继承接口

接口隔离原则:
使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
    在抽象类中,我们可以对一些抽象方法做出基础实现;

而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现

1.多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口


2.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现




猜你喜欢

转载自www.cnblogs.com/cnhyk/p/11953419.html