大家好,我是老王。
Python 开发者可能都听说过 鸭子类型和 猴子补丁这两个词,即使没听过,也大概率写过相关的代码,只不过并不了解其背后的技术要点是这两个词而已。
最近,我在面试考生时,也会问到这两个概念,很多人回答得不是很好。但当我向他们解释时,我通常会意识到,“哦,就是这个。我用过了。”
[En]
Recently, when I interviewed candidates, I would also ask about these two concepts, and many people did not answer very well. But when I explain to them, it’s common for me to realize, “Oh, this is it. I’ve used it.”
所以我决定写一篇关于这两种技术的文章。
[En]
So I decided to write an article about these two technologies.
鸭子类型
引用维基百科的一段解释:
[En]
To quote an explanation from Wikipedia:
鸭子类型( duck typing)在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由”当前方法和属性的集合”决定。
更通俗一点的说:
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
换句话说,在鸭子类型中,重点是对象的行为和它可以做什么,而不是对象所属的类型。
[En]
In other words, in the duck type, the focus is on the behavior of the object and what it can do, rather than on the type to which the object belongs.
让我们来看一个更生动地展示它的例子:
[En]
Let’s look at an example to show it more vividly:
这是一个鸭子(Duck)类
class Duck:
def eat(self):
print("A duck is eating...")
def walk(self):
print("A duck is walking...")
这是一个狗(Dog)类
class Dog:
def eat(self):
print("A dog is eating...")
def walk(self):
print("A dog is walking...")
def animal(obj):
obj.eat()
obj.walk()
if __name__ == '__main__':
animal(Duck())
animal(Dog())
程序输出:
Python 是一门动态语言,没有严格的类型检查。只要 Duck
和 Dog
分别实现了 eat
和 walk
方法就可以直接调用。
再比如 list.extend()
方法,除了 list
之外, dict
和 tuple
也可以调用,只要它是可迭代的就都可以调用。
看过上例之后,应该对「 对象的行为」和「 对象所属的类型」有更深的体会了吧。
更重要的是,除了没有明确定义任何接口之外,Duck类型实际上类似于接口。
[En]
A little bit more, the duck type is actually similar to the interface, except that there is no explicit definition of any interface.
比如用 Go 语言来实现鸭子类型,代码是这样的:
package main
import "fmt"
// 定义接口,包含 Eat 方法
type Duck interface {
Eat()
}
// 定义 Cat 结构体,并实现 Eat 方法
type Cat struct{}
func (c *Cat) Eat() {
fmt.Println("cat eat")
}
// 定义 Dog 结构体,并实现 Eat 方法
type Dog struct{}
func (d *Dog) Eat() {
fmt.Println("dog eat")
}
func main() {
var c Duck = &Cat{}
c.Eat()
var d Duck = &Dog{}
d.Eat()
s := []Duck{
&Cat{},
&Dog{},
}
for _, n := range s {
n.Eat()
}
}
通过显式定义一个 Duck
接口,每个结构体实现接口中的方法来实现。
猴子补丁
猴子补丁( Monkey Patch)的名声不太好,因为它会在运行时动态修改模块、类或函数,通常是添加功能或修正缺陷。
猴子补丁在内存中工作,不修改源代码,因此它只对当前运行的程序实例有效。
[En]
The monkey patch works in memory and does not modify the source code, so it is only valid for the currently running program instance.
然而,如果被滥用,将使系统难以理解和维护。
[En]
However, if abused, it will make the system difficult to understand and maintain.
主要有两个问题:
因此,它被视为一种临时解决方法,而不是推荐的集成代码的方法。
[En]
Therefore, it is seen as a temporary workaround, not the recommended way to integrate code.
按照惯例,让我们举一个例子来说明:
[En]
According to convention, let’s give an example to illustrate:
定义一个Dog类
class Dog:
def eat(self):
print("A dog is eating ...")
在类的外部给 Dog 类添加猴子补丁
def walk(self):
print("A dog is walking ...")
Dog.walk = walk
它的调用方式与类内部定义的属性和方法相同<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>It is called in the same way as the properties and methods defined internally in the class</font>*</details>
dog = Dog()
dog.eat()
dog.walk()
程序输出:
A dog is eating ...
A dog is walking ...
这里相当于在类的外部给 Dog
类增加了一个 walk
方法,而调用方式与类的内部定义的属性和方法一样。
再举一个比较实用的例子,比如我们常用的 json
标准库,如果说想用性能更高的 ujson
代替的话,那势必需要将每个文件的引入:
import json
改成:
import ujson as json
如果以这种方式改变,成本会更高。此时,您可以考虑使用猴子贴片,只需添加:
[En]
If it is changed in this way, the cost will be higher. At this point, you can consider using the monkey patch, just add:
import json
import ujson
def monkey_patch_json():
json.__name__ = 'ujson'
json.dumps = ujson.dumps
json.loads = ujson.loads
monkey_patch_json()
这样在以后调用 dumps
和 loads
方法的时候就是调用的 ujson
包,还是很方便的。
但猴子补丁是一把双刃剑,上面也提到了这个问题。如有必要,请谨慎使用。
[En]
But the monkey patch is a double-edged sword, and the problem is also mentioned above. If necessary, use it carefully.
以上就是本文的全部内容。如果您觉得不错,欢迎点赞、转发和关注。谢谢您一直鼓励我。
[En]
The above is the whole content of this article. If you think it is good, welcome * like * , * forward * and * follow * . Thank you for your support.
推荐阅读:
Original: https://www.cnblogs.com/alwaysbeta/p/16133086.html
Author: yongxinz
Title: Python 中的鸭子类型和猴子补丁
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/500000/
转载文章受原作者版权保护。转载请注明原作者出处!