《流暢的python》讀書筆記

流暢的python

第1章 python數據模型

---1.1 一摞Python風格的紙牌

  • 特殊方法,即__method__,又被稱爲魔術方法(magic method)或者雙下方法(dunder-method).html

  • 特殊方法的存在是爲了被python解釋器調用的python

  • collections.namedtuple用於構建一個只有少數屬性可是沒有方法的對象算法

  • 經過實現__getitem__,可使對象有[]操做,支持切片操做,可迭代編程

  • for i in x:其實是用了iter(x),而這個函數背後則是x.__iter__()方法設計模式

  • random.choice能夠從一個序列中隨機選出一個元素數組

  • 經過實現一個針對對象的key函數,就能夠對該對象進行排序安全

  • 實現特殊方法來利用python模型的好處是:多線程

    1. 類有統一的標準操做名稱閉包

    2. 能夠更加方便地利用python的標準庫app

---1.2 如何使用特殊方法

  • __repr__使對象有一個字符串表示形式.它與__str__的區別是,後者在str()函數或是print()函數被使用.若是一個對象沒有__str__函數,python會用__repr__代替.

  • __add__, __sub__, __mul__, __truediv__對應+-*\四種算數運算符

  • 默認狀況下,自定義的類的實例總被認爲是True,除非對__bool____len__有實現.bool(x)會先調用前者,再調用後者,0爲False,不然爲True.

---1.3 特殊方法一覽

第2章 序列構成的數組

---2.1 內置序列類型概覽

  • 容器序列能夠存放不一樣類型的數據,存放的是所包含的對象的引用:list,tuple,collections.deque

  • 扁平序列只能容納一種類型,存放的是值,是一段連續的內存空間:str,bytes,bytearray,memoryview,array.array

  • 可變序列:list,bytearray,array.array,collections.deque,memoryviw

  • 不可變序列:tuple,str,bytes

---2.2 列表推導和生成器表達式

  • 最好只用列表推導來生成列表,而用生成器表達式來初始化元組,數組或其餘序列類型.由於生成器表達式能夠逐個產出元素,更節省內存.

---2.3 元組不只僅是不可變的列表

  • 元組除了用做不可變列表,還能夠用於沒有字段名的記錄,其中的每一個元素都存放了記錄中一個字段的數據和位置

  • 元組拆包(例如平行賦值,函數返回多個值)可應用到任何可迭代對象上,只要被可迭代對象中的元素數量和接受這些元素的元組的空擋數一致.

  • *前綴的變量名能夠處理元組拆包中的剩餘元素

  • collections.namedtuple能夠用來構建一個帶字段名的元組和一個有名字的類.

  • 元組支持除了增減元素相關的方法以外的其餘全部列表的方法.

---2.4 切片

  • 若是賦值的對象是一個切片,那麼賦值語句的右側必須是個可迭代對象,即便是單個值也要轉換成可迭代序列.

---2.5 對序列使用+和*

  • +和*操做不會修改序列,而會新建一個包含一樣類型數據的序列做爲拼接的結果.

  • 注意:若是a*n中的a是其餘可變對象的引用的話,它們會指向同一個可變對象.正確的方式是[[] * m for i in range(n)]

---2.6 序列的增量賦值

  • +=(*=)對應的特殊方法是__iadd__(__imul__),若是沒有被實現,解釋器會退一步調用__add__.可變序列通常都實現了__iadd__

  • 可變序列的增量賦值會直接追加到原序列上,不可變序列則會建立一個新對象,把原來的元素複製進去後再追加新的元素,所以效率很低.可是str是不可變序列中的一個例外.

  • 下列語句會成功改變t的值,同時拋出異常.所以最好不要把可變對象放在元組裏.

t= (1, 2, [3, 4])
t[2] += [5,6]

---2.7 list.sort方法和內置函數sorted

  • list.sort方法會就地排序.所以它返回None,這也是python中函數或者方法對對象進行就地改動時的慣例.

  • sorted能夠接受任何可迭代對象做爲參數,並新建一個列表做爲返回值.

---2.8 用bisect來管理已排序的序列

  • bisect模塊提供了二分查找算法.包含兩個主要函數:bisect用於搜索,insort用於插入.

---2.9 當列表不是首選時

  • 若是須要一個只包含數字的列表,那麼數組(array.array)比list更高效.建立數組須要一個類型碼,表示在底層的C語言應該存放怎樣的數據類型.以二進制文件讀寫數據會顯著提升效率.

  • 內存視圖(memoryview)能在不復制內容的狀況下操做同一個數組的不一樣切片.

  • 雙向隊列(collections.deque)是一個線程安全,能夠快速從兩端添加或者刪除元素的數據類型.能夠在多線程程序中看成先進先出的棧使用.

第3章 字典和集合

---3.1 泛映射類型

  • 若是一個對象是可散列的,那麼在這個對象的生命週期中,它的散列值是不變的,並且這個對象須要實現__hash____eq__方法.若是兩個可散列對象是相等的,那麼它們的散列值必定同樣.str,bytes,數值類型,frozenset以及只包含可散列類型的元組都是科三咧類型.

---3.2 字典推導

  • 字典推導相似於列表推導,能夠從任何以鍵值對做爲元素的可迭代對象中構建出字典.

---3.3 常見的映射方法

  • 當須要經過查找來插入新值的時候,dict.setdefault比if語句要高效不少.

---3.4 映射到彈性鍵查詢

  • collections.defaultdict爲不存在的鍵提供了一個默認值.

  • 還能夠經過繼承dict並實現__missing__方法來處理鍵不存在的狀況.

---3.6 子類化UserDict

  • 若是須要創造自定義映射類型,繼承UserDict是一個很好的選擇.

---3.7 不可變映射類型

  • 若是不能讓用戶錯誤地修改某個映射,可使用types.MappinrgProxyType.將一個字典傳給它,它會動態返回原字典的改動,可是不能經過它作任何修改.

---3.8 集合論

  • 集合的本質是許多惟一對象的彙集,能夠用於去重.

  • 集合中的元素必須是可散列的,set自己是不可散列的,可是frozenset是能夠散列的.

  • 集合包含不少中綴運算符,|取合集,&取交集,-取差集.

  • 一樣能夠用集合推導式來建立集合

---3.9 dict和set的背後

  • 散列表是一個稀疏數組,python會設法保證大概有1/3的表元是空的.在快要達到閾值的時候,原有的散列表會被複制到一個更大的空間裏面.

  • 散列值應該在索引空間中儘可能分散,所以越是類似但不相等的對象,它們散列值的差異應該越大.實際運用中,多數搜索過程當中並不會有衝突發生.

  • 字典在內存上的開銷巨大,所以最好使用元組來存放大量數據.

  • 字典鍵的次序取決於添加順序,往裏面添加新鍵可能會改變已有的次序,所以不要對字典同時進行迭代和修改.

  • 上述特色,對集合也幾乎都適用.

第4章 文本和字節序列

---4.1 字符問題

  • Unicode中字符的標識(碼位)是0~1114111的數字,以4~6個16進制數字表示,並且加前綴"U+".字符的具體表述取決於所用的編碼.

---4.2 字節概要

  • python3的bytes或bytearray對象的各個元素是介於0-255之間的整數,可是它們的切片還是同一類型的二進制序列.

  • 二進制序列可能以ASCII字符自己,轉義序列和十六進制轉義序列三種形式顯示.

---4.3 基本的編解碼器

  • python自帶超過100種編解碼器.

  • UTF編碼設計的目的就是處理每個Unicode碼位.

---4.4 瞭解編碼問題

  • 多數非UTF編解碼器只能處理Unicode字符的一小部分,此時在編碼時會拋出UnicodeEncodeError.

  • 不是每個字符序列都是有效的UTF編碼,若是沒法轉換時會拋出UnicodeDecodeError.

---4.5 處理文本文件

  • 處理文本的最佳方法是:儘早把輸入解碼成字符串,儘可能晚地把字符串編碼成字節序列.python3的open,read和write方法已經幫忙實現了這個原則.

  • 須要在多臺設備中或多種場合下運行的代碼,打開文件時必定要明確傳入encoding,由於不一樣設備的默認編碼可能不一樣.

---4.6 爲了正確比較而規範化Unicode字符串

  • Unicode有組合字符(變音符號和附加到前一個字符上的記號),因此字符串比較起來很複雜.問題點解決方案是使用unicodedata.normaliza進行規範化.通常選取NFC

第5章 一等函數

---5.1 把函數視做對象

  • 在python中,函數是一等對象,函數對象自己是function類的實例.

---5.2 高階函數

  • 接受函數爲參數,或者把函數做爲結果返回的函數是高階函數,例如map,filter.最好使用列表推導或生成器表達式替代它們.

---5.3 匿名函數

  • lambda能夠建立匿名函數,通常用於做爲參數傳給高階函數.它只是語法糖,實際上也會建立函數對象.

---5.5 用戶定義的可調用類型

  • 任何python對象均可以被調用,只要實現了__call__

---5.7 從定位參數到僅限關鍵字參數

  • 在函數參數中使用*和**能夠展開參數並捕獲.若是把參數放在前面有*的參數後面,就成爲了僅限關鍵詞參數.

---5.10 支持函數式編程的包

  • operator模塊爲多個算數運算符提供了對應的函數,還有itemgetter和attrgetter能夠從序列中取出元素.

第6章 使用一等函數實現設計模式

---6.1 案例分析:重構"策略"模式

  • 策略模式是指:定義一系列算法,把它們一一封裝起來,而且使它們能夠相互替換.

  • 能夠把策略函數做爲參數傳入對象,簡化策略模式.

  • 可使用globals來獲取全部的策略函數,但要求策略函數有一個統一命名規則.

第7章 函數裝飾器和閉包

---7.1 裝飾器基礎知識

  • 裝飾器是可調用的對象,其參數是被裝飾的函數.裝飾器一般把函數替換成另外一個函數.

---7.2 Python什麼時候執行裝飾器

  • 函數裝飾器在導入模塊時當即執行,而被裝飾的函數只在明確調用時運行.

---7.3 使用裝飾器改進"策略"模式

  • 在上一章中,若是用promos列表存儲策略函數,可能會存在忘記添加的問題.若是使用一個裝飾器函數來將策略函數添加到列表中,並將每個策略函數都用這個裝飾器函數來裝飾,就能夠解決上述問題.

---7.4 變量做用域規則

  • python編譯函數的定義體時,會將在函數中賦值的變量判斷爲局部變量.若是須要讓解釋器把變量當成全局變量,須要在函數內用global聲明.

---7.5 閉包

  • 閉包指延伸了做用域的函數,其中包含函數定義體中引用,可是不在定義體中定義的非全局變量.

  • 未在本地做用域中綁定的變量叫自由變量.

  • 閉包是一種函數,它會保留定義函數時存在的自由變量的綁定,這樣調用函數時,雖然定義做用域不可用了,可是仍能使用那些綁定.

---7.6 nonlocal聲明

  • 在7.5的例子中,實際上利用了列表是可變的對象.若是想用兩個數值count和total來實現,那麼在+=時至關於賦值,因而它們會變成局部變量,而不是自由變量.

  • 爲了解決這個問題,須要使用nonlocal聲明.它的做用是把變量標記爲自由變量.

  • 另外一種實現方式是,把內部函數須要修改的變量存儲偉可變對象的元素或屬性,而且把那個對象綁定給一個自由變量.

---7.8 標準庫中的裝飾器

  • functools.lru_cache能夠把耗時的函數的結果保存起來,避免傳入相同的參數時重複計算.能夠用於優化遞歸算法.注意lru_cache使用字典存儲結果,因此被它裝飾的函數的全部參數都必須是可散列的.

  • functools.singledispatch能夠把總體方案拆分紅多個模塊,也就是將普通函數變成泛函數:根據第一個參數的類型,以不一樣方式執行相同操做.

---7.9 疊放裝飾器

  • 把@d1和@d2兩個裝飾器按順序應用到f函數上,至關於f=d1(d2(f))

---7.10 參數化裝飾器

  • 經過裝飾器工廠函數,將真正的裝飾器放在內部,就能夠接受除了函數之外的參數.

第8章 對象引用,可變性和垃圾回收

---8.1 變量不是盒子

  • 對於面嚮對象語言中的引用式變量,它們是附加在對象上的標註,而不是盒子.

  • 建立對象以後纔會把變量分配給對象.對象在賦值語句右邊建立或獲取,而後左邊的變量纔會綁定到對象上.

相關文章
相關標籤/搜索