Python技术迷

什么是Python 中的鸭子类型(Duck Typing)?

晚上十一点多在我爸的麻将桌旁边,边撸代码边看小侄子写Python作业,你们别笑啊,小学生现在Python都学得比我早——我那会儿还在折腾C++呢。结果他突然问我,“东哥,Python为啥不用声明类型呀?我写个def add(a, b): return a + b,随便啥都能加,好神奇!”我说这不就是“鸭子类型”嘛。其实我当时还没反应过来,他说的事儿,就是典型的鸭子类型场景。

鸭子类型是个啥玩意儿?

你想啊,Python里,啥都不用声明,你给我个参数,我直接用就完事了。为啥叫鸭子类型?有一句经典的话:“If it walks like a duck and quacks like a duck, it must be a duck.”就是说,你不用在乎它到底是不是个鸭子,只要它像鸭子一样走、像鸭子一样叫,那它就是个鸭子。翻译成程序员的话,就是“不在乎你对象是什么类型,只要能用就行”。你用过Java的还得搞一大堆接口,Python就直接开搞。这个事儿太常见了。

我记得前阵子和我们组小李在公司楼下抽烟,他还问我:“东哥你觉得鸭子类型靠不靠谱啊?会不会埋雷?”其实你要说靠谱吧,确实写起来很爽,但有时候也容易出锅。你比如说下面这个代码:

defmake_noise(animal):
    animal.quack()

你看这个make_noise方法,它不在乎animal到底是什么东西——只要你传进来的有quack()方法就行。你可以传个鸭子,传个实现了quack()方法的玩具青蛙,甚至传个mock对象都行——谁管你真鸭假鸭,对吧?这就是Python的日常。

生活里到处都是鸭子类型

你们在写代码的时候,反正我啊,经常写那种用requests发请求的东西,你看requests,它不管你给的参数是str,还是dict,还是file-like object,你只要能用就行,它就能发出去,这种包一用你就懂鸭子类型了。再比如你平时写自定义的file-like对象,其实只要实现了read()和write()方法,Python就能当成文件来用,完全不用继承什么File类。就像这段,随便搞一个带read的:

classMyReader:
defread(self):
return"随便读点啥"

defprint_content(reader):
    print(reader.read())

print_content(MyReader())

这段你要是拿给Java同事看,他估计要问你“你为啥不继承接口?”Python压根就没管你是啥类型,能read()就行。生活中其实很多东西都像这样,大家能用一个“协议”合作,反而更灵活,不被类型绑死。

但是你别以为它啥缺点都没有,有一回我们群里有人问我,“鸭子类型是不是就不用写测试了?”我说你可别想得太美。你写代码的时候爽,但运行时出错的概率也高。比如你传了个没quack()方法的对象进来,直接就崩了,连点预警都没有。你要是用Java,编译器直接报错,Python就是你运行到那儿才知道错在哪。这个锅有时候真挺难查的。

所以我平时有个习惯,稍微严肃点的项目,我会写点hasattr(obj, 'xxx'),先判断一下,再用。不然真被人怼了——那就只能认栽。

总结下,怎么用鸭子类型

你说鸭子类型到底该怎么用?我觉得你别怕它,Python本来就是这么个哲学,“只要像鸭子就是鸭子”,你要是写点小工具,写点业务代码,别上来给自己上枷锁,直接大胆用。写多了自然就顺了,等哪天出锅了——哎,也别怕,就查查日志,多写几个异常捕获。

最后,真心话,写鸭子类型多了之后,你会发现,代码灵活度提升不少,维护起来也方便。当然,你要是真要用到接口非常明确的场合,比如多人协作、项目特别大,建议还是适当写写文档或者类型注解(Python3的类型注解),不然真容易出幺蛾子。

-END-

我为大家打造了一份RPA教程,完全免费:https://www.songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领