第7.3节 Python特色的面向对象设计:协议、多态及鸭子类型

Python是一种多态语言,其表现特征是:对象方法的调用方只管方法是否可调用,不管对象是什么类型,从而屏蔽不同类型对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
一、    Python中的协议
Python中定义某种类型是以实现了该类型对应的协议为标准的,如 可迭代对象(Iterable) 是指实现了可迭代协议的对象类型,迭代器是实现了迭代器协议的对象类型,序列是实现了序列协议的对象类型,那么具体协议是什么呢?在Python中协议是指一个或一组可供外部访问对象的特定方法(也有把对象属性作为协议的一部分,但老猿并不推荐这种使用方式),如可迭代对象是指容器中的元素可以通过__iter__( )方法或__getitem__( )方法访问,迭代器协议必须是定义和实现了__iter__()方法和next()方法,序列协议必须实现__len__()和getitem()方法。
协议是正式的,没有强制力,可以根据具体场景实现一个具体协议的一部分。例如,为了支持迭代,只需实现__getitem__,不需要实现__len__。

二、    “鸭子类型”与多态
从Python中协议的使用可以得出,Python中的对象类型不是简单的以类型定义区分的,而是以对象是否提供了特定的访问方法来确认的。凡是实现了类型要求的方法的对象都可以看做遵守类型对应协议,就是该类型的对象,老猿理解这是Python中多态的本质,这种以协议确认类型的方法所确认的类型就是“鸭子类型”(Duck typing)。
为什么说是“鸭子类型”呢?这个所有关于“鸭子类型”介绍的文章都有共同的介绍,在这复述一下:
鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。
鸭子类型这一名字出自美国James Whitcomb Riley(有的材料说他是诗人,有的说他是测试人员,在此就不探究了)提出的如下的表述:
“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
简单归纳就是:对象的类型不再由继承等方式决定,而由实际运行时所表现出的具体行为来决定。
以下为定义的鸭子类型与人类型,它们都有相同的方法:
class Duck():
  def walk(self):
    print('I walk like a duck')
  def swim(self):
    print('I swim like a duck')
  def quack(self):
    print('I quack like a duck')


class Person():
  def walk(self):
    print('this one walk like a duck') 
  def swim(self):
    print('this man swim like a duck')
def quack(self):
    print('I quack like a duck')

类的定义语法在后面一节介绍,但大家应该明白上述两个类的定义。可以很明显的看出,Person类拥有跟Duck类一样的方法,当有一个函数调用Duck类,并利用到了三个方法walk()、swim()和quack()。我们传入Person类也一样可以运行,函数并不会检查对象的类型是不是Duck,只要他拥有这三个方法,就可以正确的被调用。
为什么Python的多态是由“鸭子类型”来实现呢?这是由于Python属于动态语言,Python在定义变量时不指定变量的类型,而是由解释器根据变量内容推断变量类型的(也就是说变量的类型取决于所关联的对象),因此Python在判断变量类型时是看变量所具有的功能(方法)来决定。这就造就了Python的这种非常神奇的多态和“鸭子类型”,也是开发者很喜欢的一种开放的程序开发风格。

三、    鸭子类型的优点
1.    python中的鸭子类型允许我们使用任何提供所需方法的对象,而不需要迫使它成为一个子类;
2.    因为任何提供正确接口的对象都可以在python中交替使用,它减少了多态的一般超类的需求。继承仍然可以用来共享代码,但是如果所有被共享的都是公共接口,鸭子类型就是所有所需的。这减少了继承的需要,同时也减少了多重继承的需要;
3.    调用方只管调用,不管细节,不管对象是什么类型,从而可以屏蔽不同类型对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。

三、    鸭子类型编程的注意事项
1.    “鸭子类型”关注是对象的方法,而不是类型,因此使用Python开发时,尽量不要使用诸如type、issubclass等函数显式地执行类型检查进行类型检查;
2.    在进行方法调用时,对于程序无法明确确认对象是否有相应方法时,可以使用异常捕获或者hasattr或callable检查所需的方法是否存在来确保对应方法可以正常调用。如果有个对象p,我们需要判断p是否有swim方法,有如下方式:
1)    使用hasattr,语句:if hasattr(p,'swim') :p.swim()
2)    使用callable,语句:if hasattr(p,'swim')  and  callable(p.swim):p.swim()
3)    使用callable,语句:if  callable(getattr(p,'swim',None)):p.swim()

本节介绍了Python中协议和“鸭子类型”的概念,并说明了Python多态与“鸭子类型”的关系,同时分析了“鸭子类型”有点以及使用的注意事项。
老猿Python(https://blog.csdn.net/LaoYuanPython)系列文章用于逐步介绍老猿学习Python后总结的学习经验,这些经验有助于没有接触过Python的程序员可以很容易地进入Python的世界。
欢迎大家批评指正,谢谢大家关注!
 

  • 22
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LaoYuanPython

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值