开篇语
1.1 面向对象
在讲鸭子类型之前先聊聊面向对象编程(Object Oriented Programming,OOP),是在程序开发中一件很强大的武器。它把对象作为程序的基本单元,一个对象包含对象变量和对象函数(或叫方法)。
收!(手动摆手)基本概念就不再展开了,简单聊聊OOP中的两个重要概念继承和多态。
1.2 继承
继承可以让子类获得父类的全部功能,父类实现过的方法,子类不需要重新定义就能自动拥有。
当然也可以为子类增加新的方法,这些新方法父类不会掌握。
如果子类新定义的方法与父类的方法相同,则子类的方法覆盖父类的方法。在程序运行时总是会调用子类的方法而不是父类的方法。
1.3 多态
在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行。
比如参数为Bird
的一个函数fly()
,新增一个继承自Bird
的子类叫Duck
,当我们传入Duck
的实例时,fly()
仍然可以正常执行。
对于一个对象,我们只需要知道它是Bird
类型,无需确切地知道它的子类型,就可以放心地调用其拥有的方法,而调用的方法是作用在Bird
还是Duck
抑或是诸如Chicken
其他子类上,由该对象拥有的实际生效方法决定,这就是多态。
总结:调用方只管调用,不管细节,不管原来的代码是如何调用的。
这就是“开闭”原则,对扩展开放:允许新增子类;对修改封闭:不需要修改依赖该类型的函数。
把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
正题
终于要讲到本文的主角鸭子类型了。
Duck typing 这个概念来源于美国印第安纳州的诗人詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-
1916)的诗句:”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')
class Person():
def walk(self):
print('this one walk like a duck')
def swim(self):
print('this man swim like a duck')
可以很明显的看出,Person
类拥有跟Duck
类一样的方法,当有一个函数调用Duck
类,并利用到了两个方法walk()
和swim()
。我们传入Person
类也一样可以运行,函数并不会检查对象的类型是不是Duck
,只要他拥有walk()
和swim()
方法,就可以正确的被调用。
再举例,如果一个对象实现了__getitem__
方法,那python的解释器就会把它当做一个collection
,就可以在这个对象上使用切片,获取子项等方法;如果一个对象实现了__iter__
和next
方法,python就会认为它是一个iterator
,就可以在这个对象上通过循环来获取各个子项。
后话
面向对象一直以来都算是我的弱项,在写代码的实际工作中,我更倾向于写函数而不是写类,这一点应该慢慢改正。毕竟OOP的确有它十分强大先进的理念,能够很大程度上提高代码的复用与可读性。