学习过程中总会遇到一些从字面上看起来很复杂,而事实上很简单的知识,Python中的鸭子类型和Monkey Patch就是这样的知识。

鸭子类型

什么是鸭子类型?

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

鸭子类型是编程语言中动态类型语言中的一种设计风格,一个对象的特征不是由父类决定,而是通过对象的方法决定的。

静态语言中对象的特性取决于其父类。而动态语言则不一样,比如迭代器,任何实现了 **iter** 和 **next**方法的对象都可称之为迭代器,但对象本身是什么类型不受限制,可以自定义为任何类。

简而言之:

一个函数F期望接收类A的实例化对象,在Python中给F传入对象时,我们传入的对象可以不是类A的实例化,只要传入入一个与类A有相同的方法接口的类的实例化对象就行(不一定所有接口相同,需要用到的方法相同就行);如果在Java/C++中就必须传入类A或者类A的子类的实例化对象!

鸭子类型通常得益于”不”测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用,这既是优点也是缺点,缺点是需要通过文档才能知道参数类型,为了弥补这方面的不足,Python3.6引入了类型信息,定义变量的时候可以指定类型,例如:

img

Monkey Patch

所谓猴子补丁就是在程序运行的过程中动态的修改一些模块、类、方法,而不是在静态代码中去修改相应的实现。

简单示例:不修改time模块的静态代码,在运行时用自定义的函数替换time模块time函数

img

借用网上一个更加通俗一点的例子:

小明最喜欢吃苹果,上帝想让小明最喜欢吃的东西变为香蕉。但是小明已经出生了,上帝不可能把小明送回娘胎修改小明最喜欢吃的东西,只能去改变出生后的小明,于是上帝对小明使用了monkey patch,翻译成代码语言就是下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class XiaoMing(object):

def favorite(self):

print("apple")



class God(object):

@classmethod

def new_xiaoming_favorite(cls):

print("banana")



@classmethod

def monkey_patch(cls):

​ XiaoMing.favorite = cls.new_xiaoming_favorite



God.monkey_patch()



xiaoming = XiaoMing()

xiaoming.favorite()

\>> banana

monkey patch的应用:

gevent库通过monkey patch修改Python自带的一些标准库,

gevent的monkey可以为socket、dns、time、select、thread、os、ssl、httplib、subprocess、sys、aggressive、Event、builtins、signal模块打上的补丁,打上补丁后他们就是非阻塞的了。

如:gevent.monkey.patch_select(),gevent.monkey.patch_socket()就可以将select和socket变为非阻塞。