作為Python學習者你應該知道的Python常用技巧
1、三目操作符
2、鴨子類型(duck typing)
首先Python不支持多態,也不用支持多態,python是一種多態語言,崇尚鴨子類型。以下是維基百科中對鴨子類型得論述:
在程序設計中,鴨子類型(英語:duck typing)是動態類型的一種風格。在這種風格中,一個對象有效的語義,不是由繼承自特定的類或實現特定的接口,而是由當前方法和屬性的集合決定。這個概念的名字來源於由James Whitcomb Riley提出的鴨子測試,“鴨子測試”可以這樣表述:
“當看到一隻鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。”
在鴨子類型中,關注的不是對象的類型本身,而是它是如何使用的。
例如,在不使用鴨子類型的語言中,我們可以編寫一個函數,它接受一個類型為鴨的對象,並調用它的走和叫方法。在使用鴨子類型的語言中,這樣的一個函數可以接受一個任意類型的對象,並調用它的走和叫方法。如果這些需要被調用的方法不存在,那麼將引發一個運行時錯誤。任何擁有這樣的正確的走和叫方法的對象都可被函數接受的這種行為引出了以上表述,這種決定類型的方式因此得名。
鴨子類型通常得益於不測試方法和函數中參數的類型,而是依賴文檔、清晰的代碼和測試來確保正確使用。從靜態類型語言轉向動態類型語言的用戶通常試圖添加一些靜態的(在運行之前的)類型檢查,從而影響了鴨子類型的益處和可伸縮性,並約束了語言的動態特性(鴨子類型應避免使用type()或isinstance()等測試類型是否合法)。
in_the_forest()函數對參數duck隻有一個要求:就是可以實現quack()和feathers()方法。然而Duck類和Person類都實現了quack()和feathers()方法,因此它們的實例對象donald和john都可以用作in_the_forest()的參數,這就是鴨子類型。
可以看出,鴨子類型給予Python這樣的動態語言以多態。但是這種多態的實現完全由程序員來約束強製實現(文檔、清晰的代碼和測試),並沒有語言上的約束(如C++繼承和虛函數)。因此這種方法既靈活,又提高了對程序員的要求。
如果你想學習Python可以來這個群,首先是四七二,中間是三零九,最後是二六一,裏麵可以學習和交流,也有資料可以下載。
3、內建函數和lamda
需求:將一個字符串列表中所有滿足包含“result"字段的字符串篩選出來。
4、iterator和generator
generator返回不用return,而用yield。
iterator實現了next()方法和__iter__,__iter__方法返回它自己。而當你調用next()方法時,會返回一個值。通常,這個next值會由generator產生。
換種說法,generator是用來生成iterator的。
那麼iterator呢?generator就是用來生成iterator的:
應用的場景:下一次返回的結果依賴於上一次返回的結果。因為yield的作用是每次函數調用執行到這裏就停止了,下次調用從yield後麵的語句開始。比如說樹的遍曆之類的。
5、bind
bind應用的場景:你寫一個好多參數的函數,先bind一個參數,變成函數A,然後換種方式bind,又變成了函數B。
介紹一個庫是functools,這裏隻介紹跟bind相似的partial方法。
6、修飾器
1)decorator
修飾器的本質就是對函數做些修飾,然後返回一個函數(callable object)。也就是所謂的高階函數。因此上麵的式子直白的寫出來就是:
foo = log(foo)
foo其實就是一個log返回的callable object wrap的別名。
如果需要這樣的修飾器,我們應該怎麼寫呢?
先把(1,2)傳給decro,然後把foo傳給decro;然後你返回給我一個能接受(*args,**kargs)參數的函數:
一定要記住,foo是一個callable object。
2)wraps
試試打印下foo函數:
不是foo麼,怎麼變成"_"了?
這個時候需要前一節提到的functools,然後對你的decorator做一點小小的改動:
7、description
包含內置方法:
1. python有三個內置函數,__set__、__get__、__delete__;
2. 隻定義__get__方法,非數據描述器(non-data descriptor);
3. 定義了__delete__ 或者 __set__ 方法的叫做數據描述器(data descriptor);
以字典為例:
輸出如下:
這個例子說明幾點:
1. 對象和類都有個字典:__dict__
2. 在對象中查找不到的屬性,會從類中查找 (t.a)
3. 對象中屬性的優先級高於類中的優先級 (t.b)
4. 設置屬性(set)的時候,會在對應的dict裏增加元素 (Test.a =xxx, t.b = xxx)
5. setattr 跟 '=' 操作符,操作對象的屬性時,看起來作用是一樣的。
通常的情況下,屬性查找如此簡單:先找對象的__dict__,然後再找類的__dict__;都找不到就拋異常;
當加入所謂的descriptor的時,事情變得稍微複雜了一點點。對於一個對象的屬性,新的順序是:
1. python自動屬性 (python自動生成的屬性,比如__doc__等)
2. 在類(及其祖先類)的__dict__中查找data descriptor,如果存在,返回data descriptor中__get__方法調用的結果
3. 在對象的__dict__中查找
4. 在類(及其祖先類)的__dict__中查找non-data descriptor,存在則返回對應__get__調用的結果
5. 在類(及其祖先類)的__dict__中查找普通屬性
這樣,在原來的屬性查找順序上,我們加上了non-data descriptor和data descriptor,分別插在2、4的位置上。
回到1)中的例子,然後再看看2)中的定義,有木有發現,這其實就是一個descriptor。這個CacheProperty有什麼作用呢? 看下麵一個使用場景(這個栗子也來自clang python binding):
Config定了一個lib方法,這個方法做一些相對耗時的操作才能獲得我們想要的lib對象。比如說加載配置文件:有沒有辦法可以做到隻在第一次調用的時候加載配置文件,其他的時候都從緩存裏讀呢?
看CacheProperty 的實現:
1 首先它是一個non-data descriptor;
2. 第一次的時候,按照4)所述的查找順序,
2.1 由於Config和其對象的__dict__中沒有lib,它會走到第4步;
2.2 然後開始執行__get__方法,該方法調用lib方法,計算出lib的值value=self.wrapped(instance) (這個wrap方法在CacheProperty __init__方法中被設置self.wrapped = wrapped, 此時wrapped就是lib方法);
2.3 隨後調用setattr方法,將計算到的值set到對象的__dict__中
3. 之後調用的時候,由於對象的__dict__中已經有這個key了。直接返回對應的值就可以了。
這隻是個non-data descriptor的例子,它驗證了屬性查找中non-data descriptor順序的正確性。
對於data descriptor屬性查找,可以參看下麵一個例子:
最後更新:2017-08-13 22:53:47