Python 中的鴨子類型和猴子補丁
大家好,我是老王。
Python 開發者可能都聽說過鴨子類型和猴子補丁這兩個詞,即使沒聽過,也大概率寫過相關的代碼,只不過並不了解其背後的技術要點是這兩個字而已。
我最近在面試候選人的時候,也會問這兩個概念,很多人答的也不是很好。但是當我向他們解釋完之後,普遍都會恍然大悟:「哦,是這個啊,我用過」。
所以,我決定來寫一篇文章,探討一下這兩種技巧。
鴨子類型
引用維基百科中的一段解釋:
鴨子類型(duck typing)在程式設計中是動態類型的風格。在這種風格中,一個物件有效的語義,不是由繼承自特定的類別或實作特定的接口,而是由"當前方法和屬性的集合"決定。
更通俗一點的說:
當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。
也就是說,在鴨子類型中,關注點在於物件的行為,能作什麼;而不是專注於物件所屬的類型。
我們看一個例子,更形像地展示一下:
# 这是一个鸭子(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())
程式輸出:
A duck is eating... A duck is walking... A dog is eating... A dog is walking...
Python 是一門動態語言,沒有嚴格的類型檢查。只要 Duck 和 Dog 分別實作了 eat 和 walk 方法就可以直接呼叫。
再例如 list.extend() 方法,除了 list 之外,dict 和 tuple 也可以調用,只要它是可迭代的就都可以調用。
看過上例之後,應該對「物件的行為」和「物件所屬的類型」有更深的體會了吧。
再擴展一點,其實鴨子類型和介面挺像的,只不過沒有明確定義任何介面。
例如用 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)的名聲不太好,因為它會在運行時動態修改模組、類別或函數,通常是新增功能或修正缺陷。
猴子補丁在記憶體中發揮作用,不會修改原始碼,因此只對目前運行的程式實例有效。
但如果濫用的話,會導致系統難以理解和維護。
主要有兩個問題:
- 補丁會破壞封裝,通常與目標緊密耦合,因此很脆弱
- 打了補丁的兩個函式庫可能相互牽絆,因為第二個庫可能會撤銷第一個庫的補丁
所以,它被視為臨時的變通方案,不是集成代碼的推薦方式。
按照慣例,還是舉個例子來說明:
# 定义一个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()
程式輸出:
A dog is eating ... A dog is walking ...
這裡相當於在類別的外部為Dog 類別增加了一個walk 方法,而呼叫方式與類別的內部定義的屬性和方法一樣。
再舉一個比較實用的例子,例如我們常用的json 標準函式庫,如果說想用效能更高的ujson 取代的話,勢必需要將每個檔案的引入:
import json
改成:
import ujson as json
如果這樣改起來成本就比較高了。這個時候就可以考慮使用猴子補丁,只需要在程式入口加上:
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 包,還是很方便的。
但猴子補丁就是一把雙面刃,問題也在上文提到了,看需,謹慎使用吧。
以上是Python 中的鴨子類型和猴子補丁的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

在 Sublime Text 中運行 Python 代碼,需先安裝 Python 插件,再創建 .py 文件並編寫代碼,最後按 Ctrl B 運行代碼,輸出會在控制台中顯示。

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

在 Notepad 中運行 Python 代碼需要安裝 Python 可執行文件和 NppExec 插件。安裝 Python 並為其添加 PATH 後,在 NppExec 插件中配置命令為“python”、參數為“{CURRENT_DIRECTORY}{FILE_NAME}”,即可在 Notepad 中通過快捷鍵“F6”運行 Python 代碼。
