一名神級程序員花了半個月:把這本書的全部重點都整理好了!超長
起步
《流暢的python》是一本適合python進階的書, 裡面介紹的基本都是高級的python用法. 對於初學python的人來說, 基礎大概也就夠用了, 但往往由於夠用讓他們忘了深入, 去精通. 我們希望全面了解這個語言的能力邊界, 可能一些高級的特性並不能馬上掌握使用, 因此這本書是工作之餘, 還有餘力的人來閱讀, 我這邊就將其有用, 精妙的進階內容整理出來.
這本書有21個章節, 整理也是根據這些章節過來.
第一章: python數據模型
第二章: 序列構成的數組
這部分主要是介紹序列, 著重介紹數組和元組的一些高級用法.
序列按照容納數據的類型可以分為:
容器序列: list、tuple 和 collections.deque 這些序列能存放不同類型的數據
扁平序列 : str、bytes、bytearray、memoryview 和 array.array,這類序列只能容納一種類型.
如果按照是否能被修改可以分為:
可變序列: list、bytearray、array.array、collections.deque 和 memoryview
不可變序列 : tuple、str 和 bytes
列表推導
列表推導是構建列表的快捷方式, 可讀性更好且效率更高.
例如, 把一個字元串變成unicode的碼位列表的例子, 一般:
第三章: 字典和集合
defaultdict:處理找不到的鍵的一個選擇
當某個鍵不在映射里, 我們也希望也能得到一個默認值. 這就是 defaultdict , 它是 dict 的子類, 並實現了 __missing__ 方法.
不可變映射類型
說到不可變, 第一想到的肯定是元組, 但是對於字典來說, 要將key和value的對應關係變成不可變, types 模塊的 MappingPrype 可以做到:
第四章: 文本和位元組序列
本章討論了文本字元串和位元組序列, 以及一些編碼上的轉換. 本章討論的 str指的是python3下的.
字元問題
如果字元序列和預期不符, 在進行解碼或編碼時容易拋出 Unicode*Error的異常. 造成這種錯誤是因為目標編碼中沒有定義某個字元(沒有定義某個碼位對應的字元), 這裡說說解決這類問題的方式.
unicode文本排序
對於字元串來說, 比較的碼位. 所以在非 ascii 字元時, 得到的結果可能會不盡人意.
第五章: 一等函數
高階函數
高階函數就是接受函數作為參數, 或者把函數作為返回結果的函數. 如 map , filter , reduce等.
第六章: 使用一等函數實現設計模式
雖然設計模式與語言無關, 但這並不意味著每一個模式都能在每一個語言中使用. Gamma 等人合著的 《設計模式:可復用面向對象軟體的基礎》 一書中有 23 個模式, 其中有 16
個在動態語言中"不見了, 或者簡化了".
這裡不舉例設計模式, 因為書里的模式不常用.
第七章: 函數裝飾器和閉包
函數裝飾器用於在源碼中「標記」函數,以某種方式增強函數的行為。這是一項強大的功 能,但是若想掌握,必須理解閉包。
修飾器和閉包經常在一起討論, 因為修飾器就是閉包的一種形式. 閉包還是回調式非同步編程和函數式編程風格的基礎.
裝飾器基礎知識
裝飾器是可調用的對象, 其參數是另一個函數(被裝飾的函數). 裝飾器可能會處理被 裝飾的函數, 然後把它返回, 或者將其替換成另一個函數或可調用對象.
閉包
閉包其實挺好理解的, 當匿名函數出現的時候, 才使得這部分難以掌握. 簡單簡短的解釋閉包就是:
名字空間與函數捆綁後的結果被稱為一個閉包(closure).
如果兩個變數都是指向同一個對象, 我們通常會說變數是另一個變數的 別名 .
在==和is之間選擇運算符 ==是用來判斷兩個對象值是否相等(注意是對象值). 而 is 則是用於判斷兩個變數是否指向同一個對象, 或者說判斷變數是不是兩一個的別名, is 並不關心對象的值. 從使用上, ==使用比較多, 而 is的執行速度比較快.
函數的參數做引用時
python中的函數參數都是採用共享傳參. 共享傳參指函數的各個形式參數獲得實參中各個引用的副本. 也就是說, 函數內部的形參 是實參的別名.
這種方案就是當傳入參數是可變對象時, 在函數內對參數的修改也就是對外部可變對象進行修改. 但這種參數試圖重新賦值為一個新的對象時則無效, 因為這只是相當於把參數作為另一個東西的引用, 原有的對象並不變. 也就是說, 在函數內, 參數是不能把一個對象替換成另一個對象的.
不要使用可變類型作為參數的默認值
參數默認值是個很棒的特性. 對於開發者來說, 應該避免使用可變對象作為參數默認值. 因為如果參數默認值是可變對象, 而且修改了它的內容, 那麼後續的函數調用上都會收到影響.
del和垃圾回收
在python中, 當一個對象失去了最後一個引用時, 會當做垃圾, 然後被回收掉. 雖然python提供了 del 語句用來刪除變數. 但實際上只是刪除了變數和對象之間的引用, 並不一定能讓對象進行回收, 因為這個對象可能還存在其他引用.
在CPython中, 垃圾回收主要用的是引用計數的演算法. 每個對象都會統計有多少引用指向自己. 當引用計數歸零時, 意味著這個對象沒有在使用, 對象就會被立即銷毀.
符合Python風格的對象
得益於 Python 數據模型,自定義類型的行為可以像內置類型那樣自然。實現如此自然的 行為,靠的不是繼承,而是鴨子類型(duck typing):我們只需按照預定行為實現對象所 需的方法即可。
對象表示形式
每門面向對象的語言至少都有一種獲取對象的字元串表示形式的標準方式。Python 提供了 兩種方式。
Python中的把使用一個下劃線前綴標記的屬性稱為"受保護的"屬性
使用slots類屬性節省空間
第十一章: 介面:從協議到抽象基類
這些協議定義為非正式的介面, 是讓編程語言實現多態的方式. 在python中, 沒有 interface關鍵字, 而且除了抽象基類, 每個類都有介面: 所有類都可以自行實現 __getitem__ 和__add__ .
有寫規定則是程序員在開發過程中慢慢總結出來的, 如受保護的屬性命名採用單個前導下劃線, 還有一些編碼規範之類的.
協議是介面, 但不是正式的, 這些規定並不是強制性的, 一個類可能只實現部分介面, 這是允許的.
既然有非正式的協議, 那麼有沒有正式的協議呢? 有, 抽象基類就是一種強制性的協議.
抽象基類要求其子類需要實現定義的某個介面, 且抽象基類不能實例化.
Python文化中的介面和協議
引入抽象基類之前, python就已經非常成功了, 即使現在也很少使用抽象基類. 通過鴨子類型和協議, 我們把協議定義為非正式介面, 是讓python實現多態的方式.
另一邊面, 不要覺得把公開數據屬性放入對象的介面中不妥, 如果需要, 總能實現讀值和設值方法, 把數據屬性變成特性. 對象公開方法的自己, 讓對象在系統中扮演特定的角色. 因此, 介面是實現特定角色的方法集合.
序列協議是python最基礎的協議之一, 即便對象只實現那個協議最基本的一部分, 解釋器也會負責地處理.
水禽和抽象基類
鴨子類型在很多情況下十分有用, 但是隨著發展, 通常由了更好的方式.
近代, 屬和種基本是根據表型系統學分類的, 鴨科屬於水禽, 而水禽還包括鵝, 鴻雁等. 水禽是對某一類表現一致進行的分類, 他們有一些統一"描述"部分.
因此, 根據分類的演化, 需要有個水禽類型, 只要 cls 是抽象基類, 即 cls的元類是abc.ABCMeta , 就可以使用 isinstance(obj, cls) 來進行判斷.與具類相比, 抽象基類有很多理論上的優點, 被註冊的類必須滿足抽象基類對方法和簽名的要求, 更重要的是滿足底層語義契約.
標準庫中的抽象基類
大多數的標準庫的抽象基類在 collections.abc 模塊中定義. 少部分在 numbers 和 io 包中有一些抽象基類. 標準庫中有兩個 abc 模塊, 這裡只討論 collections.abc .
這個模塊中定義了 16 個抽象基類.
Iterable、Container 和 Sized各個集合應該繼承這三個抽象基類,或者至少實現兼容的協議。
Iterable通過 __iter__ 方法支持迭代,Container 通過 __contains__ 方法支持 in 運算符,Sized 通過 __len__ 方法支持 len() 函數。
Sequence、Mapping 和 Set這三個是主要的不可變集合類型,而且各自都有可變的子類。
MappingView在 Python3 中,映射方法 .items() 、 .keys() 和 .values() 返回的對象分別是 ItemsView、KeysView 和 ValuesView 的實例。前兩個類還從 Set 類繼承了豐富的接 口。
Callable 和 Hashable這兩個抽象基類與集合沒有太大的關係,只不過因為collections.abc是標準庫中 定義抽象基類的第一個模塊,而它們又太重要了,因此才把它們放到 collections.abc 模塊中。我從未見過 Callable或 Hashable 的子類。這兩個抽象基類的主要作用是為內 置函數 isinstance提供支持,以一種安全的方式判斷對象能不能調用或散列。Iterator注意它是 Iterable 的子類。
第十二章: 繼承的優缺點
很多人覺得多重繼承得不償失, 那些不支持多繼承的編程語言好像也沒什麼損失.
子類化內置類型很麻煩
python2.2 以前, 內置類型(如list, dict)是不能子類化的. 它們是不能被其他類所繼承的, 原因是內置類型是C語言實現的, 不會調用用戶定義的類覆蓋的方法.
至於內置類型的子類覆蓋的方法會不會隱式調用, CPython 官方也沒有制定規則. 基本上, 內置類型的方法不會調用子類覆蓋的方法. 例如, dict 的子類覆蓋的 __getitem__ 方法不會覆蓋內置類型的 get() 方法調用.
多重繼承和方法解析順序
任何實現多重繼承的語言都要處理潛在的命名衝突,這種衝突由不相關的祖先類實現同名 方法引起。這種衝突稱為「菱形問題」,如圖.
Python 會按照特定的順序遍歷繼承 圖。這個順序叫方法解析順序(Method Resolution Order,MRO)。類都有一個名為 mro 的屬性,它的值是一個元組,按照方法解析順序列出各個超類,從當前類一直 向上,直到 object 類。
第十三章: 正確重載運算符
在python中, 大多數的運算符是可以重載的, 如 == 對應了 __eq__ , + 對應 __add__ .某些運算符不能重載, 如 is, and, or, and .
第十四章: 可迭代的對象、迭代器和生成器
迭代是數據處理的基石. 掃描內存中放不下的數據集時, 我們要找到一種惰性獲取數據的方式, 即按需一次獲取一個數據. 這就是 迭代器模式 .python中有 yield 關鍵字, 用於構建 生成器(generator) , 其作用用於迭代器一樣.所有的生成器都是迭代器, 因為生成器完全實現了迭代器的介面.檢查對象 x 是否迭代, 最準確的方法是調用 iter(x) , 如果不可迭代, 則拋出 TypeError異常. 這個方法比isinstance(x, abc.Iterable) 更準確, 因為它還考慮到遺留的 __getitem__方法.
可迭代的對象與迭代器的對比
我們需要對可迭代的對象進行一下定義:
使用 iter 內置函數可以獲取迭代器的對象。如果對象實現了能返回迭代器的 iter 方法,那麼對象就是可迭代的。序列都可以迭代;實現了 getitem 方 法,而且其參數是從零開始的索引,這種對象也可以迭代。
我們要明確可迭代對象和迭代器之間的關係: 從可迭代的對象中獲取迭代器.
標準的迭代器介面有兩個方法:
__next__ : 返回下一個可用的元素, 如果沒有元素了, 拋出 StopIteration異常.
__iter__ : 返回 self , 以便咋應該使用可迭代對象的地方使用迭代器.
典型的迭代器
為了清楚地說明可迭代對象與迭代器之間的重要區別, 我們將兩者分開, 寫成兩個類:
這個例子主要是為了區分可迭代對象和迭代器, 這種情況工作量一般比較大, 程序員也不願這樣寫.
生成器表達式
生成器表達式可以理解為列表推導的惰性版本: 不會迫切地構建列表, 而是返回一個生成器, 按需惰性生成元素. 也就是, 如果列表推導是產出列表的工廠, 那麼生成器表達式就是產出生成器的工廠.
標準庫中的生成器函數
用於映射的生成器函數
合併多個可迭代對象的生成器函數
新的句法:yield from
如果生成器函數需要產出另一個生成器生成的值, 傳統的方式是嵌套的 for 循環, 例如, 我們要自己實現 chain生成器:
可迭代的歸約函數
有些函數接受可迭代對象, 但僅返回單個結果, 這類函數叫規約函數.
第十五章: 上下文管理器和 else 塊
本章討論的是其他語言不常見的流程式控制制特性, 正因如此, python新手往往忽視或沒有充分使用這些特性. 下面討論的特性有:
with 語句和上下文管理器
for while try 語句的 else 子句
上下文管理器和with塊
使用@contextmanager
第十六章: 協程
預激協程的裝飾器
協程內部如果不能處理這個異常, 就會導致協程終止.
第十七章: 使用期物處理並發
第十八章: 使用 asyncio 包處理並發
並發是指一次處理多件事。 並行是指一次做多件事。 二者不同,但是有聯繫。 一個關於結構,一個關於執行。 並發用於制定方案,用來解決可能(但未必)並行的問題。—— Rob Pike Go 語言的創造者之一
並行是指兩個或者多個事件在同一時刻發生, 而並發是指兩個或多個事件在同一時間間隔發生. 真正運行並行需要多個核心, 現在筆記本一般有 4 個 CPU 核心, 但是通常就有超過 100 個進程同時運行. 因此, 實際上大多數進程都是並發處理的, 而不是並行處理. 計算機始終運行著 100 多個進程, 確保每個進程都有機會取得發展, 不過 CPU 本身同時做的事情不會超過四件.
從期物、任務和協程中產出
避免阻塞型調用
有兩種方法能避免阻塞型調用中止整個應用程序的進程:
在單獨的線程中運行各個阻塞型操作
把每個阻塞型操作轉換成非阻塞的非同步調用使用
多線程是可以的, 但是會消耗比較大的內存. 為了降低內存的消耗, 通常使用回調來實現非同步調用. 這是一種底層概念, 類似所有並發機制中最古老最原始的那種--硬體中斷. 使用回調時, 我們不等待響應, 而是註冊一個函數, 在發生某件事時調用. 這樣, 所有的調用都是非阻塞的.
非同步應用程序底層的事件循環能依靠基礎設置的中斷, 線程, 輪詢和後台進程等待等, 確保多個並發請求能取得進展並最終完成, 這樣才能使用回調. 事件循環獲得響應後, 會回過頭來調用我們指定的回調. 如果做法正確, 事件循環和應用代碼公共的主線程絕不會阻塞.
把生成器當做協程使用是非同步編程的另一種方式. 對事件循環來說, 調用回調與在暫停的協程上調用 .send() 效果差不多.
覆蓋型與非覆蓋型描述符對比
python存取屬性的方式是不對等的. 通過實例讀取屬性時, 通常返回的是實例中定義的屬性, 但是, 如果實例中沒有指定的屬性, 那麼會從獲取類屬性. 而實例中屬性賦值時, 通常會在實例中創建屬性, 根本不影響類.
覆蓋型描述符
我們要做一個在運行時創建類的, 類工廠函數:
元類基礎知識
元類是製造類的工廠, 不過不是函數, 本身也是類. 元類是用於構建類的類 .
如有侵權請聯繫小編刪除!


※那些神級程序員張口閉口都是專業術語!聽不懂怎麼辦?全部的術語
※用Django來創建一個自己的博客
TAG:Python熱愛著 |