Hello everyone, I am Lao Wang.
Python developers may have heard of the terms duck typing and monkey patching. Even if they have not, they have probably written related code, but they do not understand the technical points behind it. Just two words.
When I was interviewing candidates recently, I would also ask about these two concepts, and many people’s answers were not very good. But after I explain it to them, they usually suddenly realize: "Oh, this is it, I have used it before."
So, I decided to write an article to discuss these two technologies.
Quoting an explanation from Wikipedia:
Duck typing is a style of dynamic typing in programming. In this style, the effective semantics of an object are determined not by inheriting from a specific class or implementing a specific interface, but by the "current set of methods and properties."
To put it more popularly:
When you see a bird walking like a duck, swimming like a duck, and quacking like a duck, then this bird can be called a duck. .
In other words, in duck typing, the focus is on the behavior of the object and what it can do; rather than on the type to which the object belongs.
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())
Program output:
A duck is eating... A duck is walking... A dog is eating... A dog is walking...
Python is a dynamic language and does not have strict type checking. As long as Duck and Dog implement the eat and walk methods respectively, they can be called directly.
Another example is the list.extend() method. In addition to list, dict and tuple can also be called, as long as it is iterable.
After reading the above example, you should have a deeper understanding of "object behavior" and "object type".
Expanding a little further, duck types are actually quite similar to interfaces, except that no interface is explicitly defined.
For example, if you use Go language to implement duck typing, the code is as follows:
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() } }
It is implemented by explicitly defining a Duck interface and each structure implementing the methods in the interface.
Monkey patch has a bad reputation because it dynamically modifies a module, class, or function at runtime, usually to add functionality or fix defects.
Monkey patches work in memory and do not modify the source code, so they are only effective for the currently running program instance.
But if abused, it will make the system difficult to understand and maintain.
There are two main problems:
, it is considered a temporary workaround and not a recommended way to integrate the code.
As usual, 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 # 调用方式与类的内部定义的属性和方法一样 dog = Dog() dog.eat() dog.walk()
Program output:
A dog is eating ... A dog is walking ...
This is equivalent to adding a walk method to the Dog class outside the class, and The calling method is the same as the internally defined properties and methods of the class.
Let’s take another more practical example, such as our commonly used json standard library. If you want to use ujson with higher performance instead, you will inevitably need to import each file:
import json
Change to:
import ujson as json
If it is changed like this, the cost will be relatively high. At this time, you can consider using monkey patches. You only need to add:
import json import ujson def monkey_patch_json(): json.__name__ = 'ujson' json.dumps = ujson.dumps json.loads = ujson.loads monkey_patch_json()
at the program entry. This way, when you call the dumps and loads methods in the future, you will call the ujson package, which is still very convenient.
But monkey patch is a double-edged sword. The problem is also mentioned above. Use it with caution depending on the need.
The above is the detailed content of Duck typing and monkey patching in Python. For more information, please follow other related articles on the PHP Chinese website!