1. 程式人生 > >設計模式以及python中的函式是第一類物件

設計模式以及python中的函式是第一類物件

我本來是想學習一下python中的設計模式,後來發現這可能是一個"偽命題",有很多書都講解java或C++中的設計模式。但很難找到一本python

為何大量設計模式在動態語言中不適用?
A. 有一種比慣用法(idiom)抽象層次高那麼一點點的東西,我們給這種東西起個名字,叫設計模式。
B. 這裡有一套總結設計模式的套路,我們可以拿著這個套路去審視各種開發活動,把找到的共性的東西按照這個模板總結出來。
C. 面向物件開發,組合優於繼承。
這幾個東西,放到什麼面嚮物件語言裡都適用,只看前兩點的話,隨便什麼程式設計活動(甚至程式設計之外的其他活動)都適用。
23個模式,是“怕你們看不明白,這裡把前面那三點應用於C++或Java這類靜態面嚮物件語言上,看看能找到哪些設計模式。”

比如說,靜態語言講究“對修改封閉,對擴充套件開放”(打了包分發的東西根本就不能改變其執行邏輯),某些動態語言根本不用管,metaClass,prototype隨便改。但允許這樣改是不是一定好,未必,得看場景。再比如,靜態語言講究面向協議程式設計,你要傳值就必須保證繼承介面或父類來承諾協議。某些動態語言根本不用管,只要傳個東西進去,在執行過程中不管三七二十一直接拿來調方法試試,如果傳進去的東西又剛好帶了個叫這個名字的方法,就呼叫成功了,否則就拋個執行期錯誤。好玩的是,人們還給這種“根本不用管typing的玩法”起了個名字叫“Duck Typing”(“我的規矩就是沒規矩,這種沒規矩的規矩叫Duck規矩”)。允許這樣玩是不是一定好,也未必,得看場景。

設計模式是為了補足靜態語言的缺陷而存在的。解決的都是其他語言裡不存在的問題,因為python已經通過新增語言特性解決了大部分這樣的缺陷:比如factory method的應用場景基本就是虛建構函式,也就是傳一個類名string,構造出該類的物件,這是python天然支援的;還有command,大部分應用場景在python裡一個lambda就搞定了。還有一部分是可有可無的,比如singleton、prototype等。
Factory解決的是物件構建和具體實現的耦合,python大多數情況下不用factory是國為本身支援類和函式做為first-class object
比如 Scala 的單例物件,Python 的 kwargs、函式第一性和內省,哪個不是設計模式的替代?

本質上來說,動態語言的多型要比靜態語言的多型更純粹。Java的多型基於interface和類繼承,可以說完全是受實現的效能要求(包括編譯和執行)的妥協。而動態語言,以Python為例,本質上來說其實只有兩個多型的點:callable和getattr
所有的callable,只要能接受相同的引數,就可以通用
所有的物件,只要能通過__getattribute__機制獲取到相同的屬性,就可以通用

作者:靈劍
連結:https://www.zhihu.com/question/63734103/answer/212353641
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

更重要的是這兩條原則不需要任何的宣告就能夠成立,這樣設計上來說要比基於interface和顯式定義簡潔得多。具體到Python,所有其他特性可以說都是在這個基礎上成立的:
類是callable,構造物件就是呼叫類
類也是物件,構造類是呼叫metaclass
許多語法特性本質上是獲取magic方法(開頭和結束)然後呼叫
獲取物件的屬性就是呼叫物件對應類的__getattribute
。__getattribute__可以認為是設計模式裡模板模式的體現,它的預設實現有很多注入點,getattr,descriptor,繼承,例項方法,類方法,靜態方法,dict__等都來自於預設實現。
因為這些特性,原來的所謂設計原則,在動態語言中並不需要特別的設計:
開閉原則:動態語言天生就是對擴充套件和修改都開放的,不需要任何特別的設計。只要一個新的物件有以前物件相同的屬性,它就可以替代以前的物件;只要一個新的callable接受相同的引數,它就可以替代以前的callable。甚至,在必要的時候,以前的實現也可以通過修改類來完全替換掉(Python中一般叫monkey patch)。
里氏代換原則:動態語言中只需要考慮Duck Type,Duck Type都可以互相替代,子類天生是Duck Type,僅此而已。
依賴倒轉原則:這個仍然是很重要的,但是在動態語言中,可以認為上層用到了哪些特性,哪些特性就是介面,不需要特意定義。這樣不管是增加還是減少都比較容易。要讓這個依賴的部分最小化,仍然需要精心設計。
介面隔離原則:同上,用到的才是介面,沒有用到的就等於不存在,可以不用實現,可以認為介面天生是最小化的。還可以通過動態檢測判斷輸入是否實現了某個介面,從而自動適應不同特性。
最少知道原則:這個仍然需要設計保證。
合成複用原則:在動態語言中,合成與繼承沒有本質的區別,無論使用哪一種都不會造成問題。合成可以通過動態屬性或者直接複製屬性來假裝自己是繼承關係,繼承也可以通過動態屬性遮蔽來自父類的介面(比如在Python中可以raise AttributeError)來假裝自己不是父類的派生類,甚至還可以不呼叫父類的__init
()。
可以看出,需要嚴格考慮得設計原則本來就要少得多,依賴倒轉和最少知道兩條足矣。因為這些特性,許多以前在Java中需要技巧實現的場景,在Python中都可以用直接的方式實現,比如:類就是自己的Factory例項可以動態增加方法,因此不需要Adaptor或者Decoratorcallable可以單獨傳遞,觀察者等模式都可以簡化

最主要原因是“函式第一性”(函式是第一類物件)。程式碼寫多了就知道了,說再多沒用。

鴨子型別,高階函式和閉包的特性,讓很多模式變得“自然”了。
動態語言的問題在哪裡,最明顯的問題就是編譯期型別檢查的缺失使得介面不再被顯式定義(最多寫到註釋裡),所以才有那句“程式碼重構火葬場”,靜態語言你把介面改了,看編譯器報錯,或者看ide紅線,就知道哪裡該改了,動態語言呢……有些地方的bug甚至執行時測試不到位的話都暴露不出來。

作者:qwerty
連結:https://www.zhihu.com/question/63734103/answer/451333487
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

給出兩個Github上設計模式的Python(動態語言),Java(靜態語言)實現faif/python-patterns :這是Python版本的iluwatar/java-design-patterns:這是Java版本的以策略模式為例,https://github.com/faif/python-patterns/blob/master/behavioral/strategy.py 這一份Python版本能利用動態語言的特性很方便的處理,而在Java中,我們可能要對interface編寫不同的實現。又比如說抽象工廠模式,Python的鴨子型別讓你完全不用弄出一堆神奇的藉口、繼承關係。順便附上網上找到的與此相關的問答Are there any design patterns that are unnecessary in dynamic languages like Python?希望對您有所幫助0.0

作者:mwish
連結:https://www.zhihu.com/question/63734103/answer/224382958
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

Python中包含函式在內的一切皆為物件,函式作為第一類物件,支援賦值給變數,作為引數傳遞給其它函式,作為其它函式的返回值,支援函式的巢狀,實現了__call__方法的類例項物件也可以當做函式被呼叫。
如何正確理解Python函式是第一類物件
1,函式可以被賦值

2,函式當成引數傳遞

3,函式緊跟括號,就執行裡面的程式碼

4,函式不帶括號,拿函式的記憶體地址

有第一類物件,當然也有第二類物件。。