<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python 中的鴨子類型和猴子補丁

          共 2489字,需瀏覽 5分鐘

           ·

          2022-04-15 15:09

          大家好,我是老王。

          Python 開發(fā)者可能都聽說過鴨子類型猴子補丁這兩個詞,即使沒聽過,也大概率寫過相關的代碼,只不過并不了解其背后的技術要點是這兩個詞而已。

          我最近在面試候選人的時候,也會問這兩個概念,很多人答的也并不是很好。但是當我向他們解釋完之后,普遍都會恍然大悟:“哦,是這個啊,我用過”。

          所以,我決定來寫一篇文章,探討一下這兩個技術。

          鴨子類型

          引用維基百科中的一段解釋:

          鴨子類型duck typing)在程序設計中是動態(tài)類型的一種風格。在這種風格中,一個對象有效的語義,不是由繼承自特定的類或實現(xiàn)特定的接口,而是由"當前方法和屬性的集合"決定。

          更通俗一點的說:

          當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子。

          也就是說,在鴨子類型中,關注點在于對象的行為,能作什么;而不是關注對象所屬的類型。

          我們看一個例子,更形象地展示一下:

          #?這是一個鴨子(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 是一門動態(tài)語言,沒有嚴格的類型檢查。只要 DuckDog 分別實現(xiàn)了 eatwalk 方法就可以直接調用。

          再比如 list.extend() 方法,除了 list 之外,dicttuple 也可以調用,只要它是可迭代的就都可以調用。

          看過上例之后,應該對「對象的行為」和「對象所屬的類型」有更深的體會了吧。

          再擴展一點,其實鴨子類型和接口挺像的,只不過沒有顯式定義任何接口。

          比如用 Go 語言來實現(xiàn)鴨子類型,代碼是這樣的:

          package?main

          import?"fmt"

          //?定義接口,包含?Eat?方法
          type?Duck?interface?{
          ?Eat()
          }

          //?定義?Cat?結構體,并實現(xiàn)?Eat?方法
          type?Cat?struct{}

          func?(c?*Cat)?Eat()?{
          ?fmt.Println("cat?eat")
          }

          //?定義?Dog?結構體,并實現(xiàn)?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 接口,每個結構體實現(xiàn)接口中的方法來實現(xiàn)。

          猴子補丁

          猴子補丁Monkey Patch)的名聲不太好,因為它會在運行時動態(tài)修改模塊、類或函數(shù),通常是添加功能或修正缺陷。

          猴子補丁在內存中發(fā)揮作用,不會修改源碼,因此只對當前運行的程序實例有效。

          但如果濫用的話,會導致系統(tǒng)難以理解和維護。

          主要有兩個問題:

          1. 補丁會破壞封裝,通常與目標緊密耦合,因此很脆弱
          2. 打了補丁的兩個庫可能相互牽絆,因為第二個庫可能會撤銷第一個庫的補丁

          所以,它被視為臨時的變通方案,不是集成代碼的推薦方式。

          按照慣例,還是舉個例子來說明:

          #?定義一個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()

          這樣在以后調用 dumpsloads 方法的時候就是調用的 ujson 包,還是很方便的。

          但猴子補丁就是一把雙刃劍,問題也在上文中提到了,看需,謹慎使用吧。

          以上就是本文的全部內容,如果覺得還不錯的話,歡迎點贊,轉發(fā)關注,感謝支持。


          推薦閱讀:

          瀏覽 43
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  看黄色毛片 | 国产色婷婷手机在线 | 天堂网av黄色 | 美女干逼视频 | 国产一级a爱做片免费看 |