Skip to content

Commit 7b836cd

Browse files
committed
add file
1 parent 5f56c97 commit 7b836cd

File tree

4 files changed

+246
-0
lines changed

4 files changed

+246
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Go 学习路线图,包括基础专栏,进阶专栏,源码阅读,实战
1313
### Python
1414

1515
- [**Python 学习路线(2022)**](https://mp.weixin.qq.com/s/CyJ92-CD1xnihlp-Dqj8Yw)
16+
- [Python 中的鸭子类型和猴子补丁](https://mp.weixin.qq.com/s/3WGFkl9MRbYjojFK7-eEww)
17+
- [我写的 Python 代码,同事都说好](https://mp.weixin.qq.com/s/shO7Vw8U3xEJelzXgCa_mQ)
1618
- [Python 高手都这样使用字典,这些高效方法你知道吗?|pythonic 小技巧](<https://github.com/yongxinz/tech-blog/blob/master/python/Python%20%E9%AB%98%E6%89%8B%E9%83%BD%E8%BF%99%E6%A0%B7%E4%BD%BF%E7%94%A8%E5%AD%97%E5%85%B8%EF%BC%8C%E8%BF%99%E4%BA%9B%E6%96%B9%E6%B3%95%E4%BD%A0%E9%83%BD%E7%9F%A5%E9%81%93%E5%90%97%EF%BC%9F%EF%BD%9Cpythonic%20%E5%B0%8F%E6%8A%80%E5%B7%A7.md>)
1719
- [像这样操作 Python 列表,能让你的代码更优雅 | pythonic 小技巧](<https://github.com/yongxinz/tech-blog/blob/master/python/%E5%83%8F%E8%BF%99%E6%A0%B7%E6%93%8D%E4%BD%9C%20Python%20%E5%88%97%E8%A1%A8%EF%BC%8C%E8%83%BD%E8%AE%A9%E4%BD%A0%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9B%B4%E4%BC%98%E9%9B%85%20%EF%BD%9C%20pythonic%20%E5%B0%8F%E6%8A%80%E5%B7%A7.md>)
1820
- [Python 求两个列表的交集,并集和差集 | pythonic 小技巧](<https://github.com/yongxinz/tech-blog/blob/master/python/Python%20%E6%B1%82%E4%B8%A4%E4%B8%AA%E5%88%97%E8%A1%A8%E7%9A%84%E4%BA%A4%E9%9B%86%EF%BC%8C%E5%B9%B6%E9%9B%86%E5%92%8C%E5%B7%AE%E9%9B%86%20%20pythonic%20%E5%B0%8F%E6%8A%80%E5%B7%A7.md>)
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
**原文链接:** [Python 中的鸭子类型和猴子补丁](https://mp.weixin.qq.com/s/3WGFkl9MRbYjojFK7-eEww)
2+
3+
大家好,我是老王。
4+
5+
Python 开发者可能都听说过**鸭子类型****猴子补丁**这两个词,即使没听过,也大概率写过相关的代码,只不过并不了解其背后的技术要点是这两个词而已。
6+
7+
我最近在面试候选人的时候,也会问这两个概念,很多人答的也并不是很好。但是当我向他们解释完之后,普遍都会恍然大悟:“哦,是这个啊,我用过”。
8+
9+
所以,我决定来写一篇文章,探讨一下这两个技术。
10+
11+
## 鸭子类型
12+
13+
引用维基百科中的一段解释:
14+
15+
> **鸭子类型****duck typing**)在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。
16+
17+
更通俗一点的说:
18+
19+
> 当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
20+
21+
也就是说,在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。
22+
23+
我们看一个例子,更形象地展示一下:
24+
25+
```python
26+
# 这是一个鸭子(Duck)类
27+
class Duck:
28+
def eat(self):
29+
print("A duck is eating...")
30+
31+
def walk(self):
32+
print("A duck is walking...")
33+
34+
35+
# 这是一个狗(Dog)类
36+
class Dog:
37+
def eat(self):
38+
print("A dog is eating...")
39+
40+
def walk(self):
41+
print("A dog is walking...")
42+
43+
44+
def animal(obj):
45+
obj.eat()
46+
obj.walk()
47+
48+
49+
if __name__ == '__main__':
50+
animal(Duck())
51+
animal(Dog())
52+
```
53+
54+
程序输出:
55+
56+
```
57+
A duck is eating...
58+
A duck is walking...
59+
A dog is eating...
60+
A dog is walking...
61+
```
62+
63+
Python 是一门动态语言,没有严格的类型检查。只要 `Duck``Dog` 分别实现了 `eat``walk` 方法就可以直接调用。
64+
65+
再比如 `list.extend()` 方法,除了 `list` 之外,`dict``tuple` 也可以调用,只要它是可迭代的就都可以调用。
66+
67+
看过上例之后,应该对「**对象的行为**」和「**对象所属的类型**」有更深的体会了吧。
68+
69+
再扩展一点,其实鸭子类型和接口挺像的,只不过没有显式定义任何接口。
70+
71+
比如用 Go 语言来实现鸭子类型,代码是这样的:
72+
73+
```go
74+
package main
75+
76+
import "fmt"
77+
78+
// 定义接口,包含 Eat 方法
79+
type Duck interface {
80+
Eat()
81+
}
82+
83+
// 定义 Cat 结构体,并实现 Eat 方法
84+
type Cat struct{}
85+
86+
func (c *Cat) Eat() {
87+
fmt.Println("cat eat")
88+
}
89+
90+
// 定义 Dog 结构体,并实现 Eat 方法
91+
type Dog struct{}
92+
93+
func (d *Dog) Eat() {
94+
fmt.Println("dog eat")
95+
}
96+
97+
func main() {
98+
var c Duck = &Cat{}
99+
c.Eat()
100+
101+
var d Duck = &Dog{}
102+
d.Eat()
103+
104+
s := []Duck{
105+
&Cat{},
106+
&Dog{},
107+
}
108+
for _, n := range s {
109+
n.Eat()
110+
}
111+
}
112+
```
113+
114+
通过显式定义一个 `Duck` 接口,每个结构体实现接口中的方法来实现。
115+
116+
## 猴子补丁
117+
118+
**猴子补丁****Monkey Patch**)的名声不太好,因为它会在运行时动态修改模块、类或函数,通常是添加功能或修正缺陷。
119+
120+
猴子补丁在内存中发挥作用,不会修改源码,因此只对当前运行的程序实例有效。
121+
122+
但如果滥用的话,会导致系统难以理解和维护。
123+
124+
主要有两个问题:
125+
126+
1. 补丁会破坏封装,通常与目标紧密耦合,因此很脆弱
127+
2. 打了补丁的两个库可能相互牵绊,因为第二个库可能会撤销第一个库的补丁
128+
129+
所以,它被视为临时的变通方案,不是集成代码的推荐方式。
130+
131+
按照惯例,还是举个例子来说明:
132+
133+
```python
134+
# 定义一个Dog类
135+
class Dog:
136+
def eat(self):
137+
print("A dog is eating ...")
138+
139+
140+
# 在类的外部给 Dog 类添加猴子补丁
141+
def walk(self):
142+
print("A dog is walking ...")
143+
144+
145+
Dog.walk = walk
146+
147+
# 调用方式与类的内部定义的属性和方法一样
148+
dog = Dog()
149+
dog.eat()
150+
dog.walk()
151+
```
152+
153+
程序输出:
154+
155+
```
156+
A dog is eating ...
157+
A dog is walking ...
158+
```
159+
160+
这里相当于在类的外部给 `Dog` 类增加了一个 `walk` 方法,而调用方式与类的内部定义的属性和方法一样。
161+
162+
再举一个比较实用的例子,比如我们常用的 `json` 标准库,如果说想用性能更高的 `ujson` 代替的话,那势必需要将每个文件的引入:
163+
164+
```python
165+
import json
166+
```
167+
168+
改成:
169+
170+
```python
171+
import ujson as json
172+
```
173+
174+
如果这样改起来成本就比较高了。这个时候就可以考虑使用猴子补丁,只需要在程序入口加上:
175+
176+
```python
177+
import json
178+
import ujson
179+
180+
181+
def monkey_patch_json():
182+
json.__name__ = 'ujson'
183+
json.dumps = ujson.dumps
184+
json.loads = ujson.loads
185+
186+
187+
monkey_patch_json()
188+
```
189+
190+
这样在以后调用 `dumps``loads` 方法的时候就是调用的 `ujson` 包,还是很方便的。
191+
192+
但猴子补丁就是一把双刃剑,问题也在上文中提到了,看需,谨慎使用吧。
193+
194+
以上就是本文的全部内容,如果觉得还不错的话,欢迎**点赞****转发****关注**,感谢支持。
195+
196+
---
197+
198+
**推荐阅读:**
199+
200+
- [**Python 学习路线(2022)**](https://mp.weixin.qq.com/s/CyJ92-CD1xnihlp-Dqj8Yw)
201+
- [我写的 Python 代码,同事都说好](https://mp.weixin.qq.com/s/shO7Vw8U3xEJelzXgCa_mQ)

python/src/duck_typing.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# 这是一个鸭子(Duck)类
2+
class Duck:
3+
def eat(self):
4+
print("A duck is eating...")
5+
6+
def walk(self):
7+
print("A duck is walking...")
8+
9+
10+
# 这是一个狗(Dog)类
11+
class Dog:
12+
def eat(self):
13+
print("A dog is eating...")
14+
15+
def walk(self):
16+
print("A dog is walking...")
17+
18+
19+
def animal(obj):
20+
obj.eat()
21+
obj.walk()
22+
23+
24+
if __name__ == '__main__':
25+
animal(Duck())
26+
animal(Dog())

python/src/monkey_patch.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# 定义一个Dog类
2+
class Dog:
3+
def eat(self):
4+
print("A dog is eating ...")
5+
6+
7+
# 在类的外部给 Dog 类添加猴子补丁
8+
def walk(self):
9+
print("A dog is walking ...")
10+
11+
12+
Dog.walk = walk
13+
14+
# 与类的内部定义的属性和方法无差异
15+
dog = Dog()
16+
dog.eat()
17+
dog.walk()

0 commit comments

Comments
 (0)