【四】面向对象之多态与鸭子类型

【四】面向对象之多态与鸭子类型

【一】多态与多态性

(1)什么是多态

  • 多态指的是一类事物有多种形态
    • 比如动物有多种形态:猫、狗、猪

(2)示例

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

(3)什么是多态性

  • 多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象
    • 这就需要在设计时,把对象的使用方法统一成一种:
    • 例如cat、dog、pig都是动物
      • 但凡是动物肯定有talk方法
      • 于是我们可以不用考虑它们三者的具体是什么类型的动物,而直接使用
>>> cat.talk()
喵喵喵
>>> dog.talk()
汪汪汪
>>> pig.talk()
哼哼哼
  • 更进一步,我们可以定义一个统一的接口来使用
>>> def Talk(animal):
...     animal.talk()
... 
>>> Talk(cat)
喵喵喵
>>> Talk(dog)
汪汪汪
>>> Talk(pig)
哼哼哼

Python中一切皆对象,本身就支持多态性

# 我们可以在不考虑三者类型的情况下直接使用统计三个对象的长度
s.__len__()
l.__len__()
t.__len__()

# Python内置了一个统一的接口
len(s)
len(l)
len(t)

(4)多态性的优点

  • 多态性的好处在于增强了程序的灵活性和可扩展性
    • 比如通过继承Animal类创建了一个新的类
    • 实例化得到的对象obj
    • 可以使用相同的方式使用obj.talk()
>>> class Wolf(Animal): #动物的另外一种形态:狼
...     def talk(self):
...         print('嗷...')
... 
>>> wolf=Wolf() # 实例出一头狼
>>> wolf.talk() # 使用者根本无需关心wolf是什么类型而调用talk
嗷...

(5)小结

  • 综上我们得知,多态性的本质在于不同的类中定义有相同的方法名
    • 这样我们就可以不考虑类而统一用一种方式去使用对象
    • 可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
import abc

# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
    def talk(self): # 抽象方法中无需实现具体的功能
        pass

class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
    def talk(self):
        pass

cat=Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化

【二】Python术语表中的鸭子类型 duck-typing

  • 什么是鸭子类型 duck-typing

    • 鸭子类型是一种编程风格,决定一个对象是否有正确的接口
      • 关注点在于它的方法或属性
      • 而不是它的类型(“如果它看起来像鸭子,像鸭子一样嘎嘎叫,那么它一定是鸭子。”)。
  • 通过强调接口而不是特定类型,设计良好的代码通过多态提高了灵活性。

    • 鸭子类型无需使用 type()isinstance() 进行检查(注意,鸭子类型可以用抽象基类来补充)
    • 相反,它通常使用 hasattr() 来检查,或是 EAFP 编程。
  • 但其实我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的“鸭子类型”(duck typing):

    • “如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。
    • 比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度,如下
# 二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
class Txt: # Txt类有两个与文件类型同名的方法,即read和write
    def read(self):
        pass
    def write(self):
        pass

class Disk: # Disk类也有两个与文件类型同名的方法:read和write
    def read(self):
        pass
    def write(self):
        pass
posted @ 2023-06-19 20:15  Chimengmeng  阅读(29)  评论(0编辑  收藏  举报
/* */