筆記之《Python學習手冊》第4版
Learning Python
(4th Edition)
Python學習手冊
第4版
Mark Lutz著
李軍 劉紅偉等譯
O’REILLY Media, Inc
機械工業出版社
其實,我原先是不準備做這個筆記的,感覺程式設計這是技術性問題,多練即可,無奈大腦記性不佳,且技術之間的關係又沒有搞清楚,故特作此筆記,以圖理清脈絡,加強記憶。
這篇筆記不會像以前那樣詳細,只是作為個人一種梳理、助記工具而已。
話說該作者挺為初學者著想的,全面詳細,美中不足的是話有些重複。
一個多星期讀完這本書,我好佩服自己啊!!!
型別和運算
- 程式由模組構成。
- 模組包含語句。
- 語句包含表示式。
- 表示式建立並處理物件。
核心資料型別
數字
字串
列表
字典
字典檢視
元組
檔案
集合
動態型別
變數在賦值是建立,可以引用任何型別的物件,並且必須在引用之前賦值
變數是一個系統表的元素,擁有指向物件的連線的空間。
物件是分配的一塊記憶體,有足夠的空間去表示它們代表的值。
引用是自動形成的從變數到物件的指標。
型別屬於物件,其頭部有一個型別識別符號和一個引用計數器。
引用計數器為零時,該物件的記憶體空間會被回收。
作為一種優化,Python會快取不變的物件(通常是小的整數和小的字串)並對其複用。
共享引用——多個變數名引用了同一個物件。當物件是不變物件時,修改其中一個變數不會影響到另外一個,但物件是可變物件時,原處修改則會影響到。
內建型別陷阱
賦值產生引用,而不是拷貝
重複能夠增加層次深度
留意迴圈資料結構
不可變型別不可以在原處改變
語句和語法
賦值、表示式和列印
賦值
賦值語句執行時,python會建立相應的臨時資料型別儲存右側變數原始的值
Python 3.0中的擴充套件序列解包,*號會使變數匹配剩下的內容
If測試和語法規則
布林運算時,有短路規則,返回的是短路時所測試的物件。
While和for迴圈
迴圈else語句用於非break語句跳出,注意其縮排。
內建的range函式返回一系列連續增加的整數,可作為for中的索引。
zip函式返回並行元素的元祖列表,可用於for中遍歷數個序列 。
enumerate函式產生偏移值及偏移值處的值
迭代器和解析,第一部分
一般情況下,“可迭代的”指支援iter的一個物件,“迭代器”值iter所返回的一個支援next(I)的物件。
for迴圈開始時,會通過它傳給iter內建函式,以便從中取得迭代器。
列表解析從語法上講,源於集合理論表示法中的一個結構【難道是我在高中學的那個】。通常其迭代是以C語言的速度執行。
文件
#註釋
dir函式
文件字串:__doc__
寫成字串,放在模組檔案、函式以及類語句的頂端的註釋
PyDoc:help函式
提取文件字串並且自動提取其結構化的資訊,並將其格式化成各種型別的排列友好的報表。
PyDoc:HTML報表
函式
函式基礎
def語句是實時執行的
作用域
作用域法則
內嵌的模組是全域性作用域。
全域性作用域的作用範圍僅限於單個檔案
每次對函式的呼叫都建立一個新的本地作用域
賦值的變數名除非宣告為全域性或非本地,否則均為本地
所有其他的變數名都快歸納為本地、全域性或內建的。
變數名解析:LEGB原則
當在函式中使用為認證的變數名時,會搜尋4個作用域,本地(L),上層函式的本地(E),全域性(G),內建(B,__builtin__標準庫模組),在第一個找到處停下。
當在函式中給一個變數名賦值時,Python總是建立或改變本地的變數名,除非已經宣告為全域性變數
在函式外給變數名賦值時,本地與全域性是相同的。
global語句
全域性變數如果在函式內被賦值的話,必須經過global宣告
全域性變數名在函式內部不經過宣告也可被引用
作用域和巢狀函式
巢狀函式能夠記住巢狀作用域的變數值,即使上層函式已經返回了。
nonlocal語句
將變數對映到上層函式作用域中的對應變數。
引數
在函式呼叫中,引數必去以此順序出現:任何位置引數(value),後面跟著任何關鍵字引數(name=value)和*sequence形式的組合,後面跟著**dict形式。
在函式頭部,引數必須以此順序出現:任何一般引數(name),緊跟著任何預設引數(name=value),如果有的話,後面是*name的形式,後面跟著name或name=value keyword-only引數,後面跟著**name形式。
引數匹配步驟
- 通過位置分配非關鍵字引數
- 通過匹配變數名分配關鍵字引數
- 其他額外的非關鍵字引數分配到*name元祖中
- 其他額外的關鍵字引數分配到**name字典中。
- 用預設值分配給在頭部未得到分配的引數。
函式的高階話題
儘量用迴圈,而不是遞迴,因為for迴圈會自動迭代。
函式註解,編寫在def頭部行。引數:註解,引數列表->註解。
匿名函式:lambda
是一個表示式,不是一個語句。
主體是一個單個的表示式,不是程式碼塊。
迭代和解析,第二部分
列表解析通用結構 [expression for target1 in iterable1 [if condtion1] for target2 in iterable2 [if condition2]…]
重訪迭代器:生成器
生成器函式:編寫為常規的def語句,但使用yield語句一次返回一個結果,在每個結果之間掛起和繼續他們的狀態。yield語句編譯為生成器,呼叫時,返回一個支援自動建立__init__方法的迭代器
生成器表示式類似於列表解析,返回按需產生結果的一個物件。括在圓括號中而不是方括號中。
模組
模組:巨集偉藍圖
import如何工作
第一次匯入指定檔案時,會執行三個步驟。1、找到模組檔案。2、編譯成位碼(需要時)。3、執行模組的程式碼來建立所定義的物件。
模組搜尋路徑
sys.path包括四個部分:
- 程式的主目錄
- PYTHONPATH目錄
- 標準連結庫目錄
- 任何.pth檔案的目錄
模組程式碼編寫基礎
import和from是賦值語句
import將整個模組物件賦給一個變數名
from將一個或多個變數名賦給另一模組中同名的物件
匯入和作用域
函式絕對無法看見其他函式內的變數名,除非它在處於這個函式內
模組程式程式碼絕對無法看見其他模組內的變數名,除非明確地進行了匯入
過載模組
reload函式強制已載入的模組程式碼重新載入並重新執行。新的程式碼的賦值語句會在適當的地方修改現有的模組物件。
模組包
Python程式碼的目錄稱為包
包和搜尋路徑設定
包所在的容器目錄應在模組搜尋路徑中
__init__.py包檔案
包匯入路徑中的每個目錄內都必須有__init__.py檔案
包首次匯入時,會自動執行該檔案
可為目錄所建立的模組物件提供名稱空間,包含所賦值的所有變數名
使用__all__列表,定義目錄以from*語句形式匯入時,需要匯出什麼。
包相對匯入
Python 3.0中的變化
修改了模組匯入搜尋路徑語義,以預設地跳過包自己的目錄。到如只是檢查搜尋路徑的其他元件。這叫做“絕對”匯入。
擴充套件了from語句的語法,以允許顯式地要求匯入值搜尋包的目錄。這叫做“相對”匯入。
高階模組話題
最小化from*的破壞:_X和__all__
_X會阻止from*匯入此變數名
__all__列表列出了from*語句要匯出的變數
Import語句和from語句的as擴充套件
若要過載from語句匯入的變數,需先過載該模組(若該模組沒有匯入的話,需先匯入,再過載),再重新執行from語句。
類和OOP
類程式碼編寫基礎
類產生多個例項物件
類物件提供預設行為
Class語句建立類物件並將其賦值給變數名
Class語句的賦值語句會建立類的屬性
類屬性提供物件的狀態和行為
例項物件是具體的元素
像函式那樣呼叫類物件會建立新的例項物件
每個例項物件繼承類的屬性並獲得了自己的名稱空間
在方法內對self屬性做賦值運算會產生每個例項自己的屬性
類通過繼承進行定製
超類列在了類開頭的括號中
類從其超類中繼承屬性
例項會繼承所有可讀取類的屬性
每個object.attribute都會開啟新的獨立搜尋
邏輯的修改是通過建立子類,而不是修改超類
類可以截獲Python運算子
以雙下劃線命名的方法(__X__)是特殊鉤子
當例項出現在內建運算時,這類方法會自動呼叫
類可覆蓋多數內建型別運算
運算子覆蓋方法沒有預設值,而且也不需要
運算子可讓Python的物件模型相整合
更多例項
步驟1:建立例項
編寫建構函式
以兩種方式使用程式碼:作為模組檔案或作為指令碼使用__name__執行測試程式碼
步驟2:新增行為方法
編寫方法
封裝,把操作邏輯包裝到介面之和
步驟3;運算子過載
提供列印顯示
步驟4:通過子類定製行為
編寫子類
擴充套件方法,呼叫超類的方法
繼承、定製和擴充套件
步驟5:定製建構函式
步驟6:使用內省工具
instance.__class__
object.__dict__
步驟7:把物件儲存到資料庫中
Pickle和Shelve
pickle:任意Python物件和位元組串之間的序列化
dbm:實現一個可通過鍵訪問的檔案系統,以儲存字串
shelve:使用另外兩個模組按照鍵把Python物件儲存到一個檔案中
類程式碼編寫細節
運算子過載
建構函式和表示式:__init__和__sub__
索引和分片:__getitem__和__setitem__
攔截分片slice()
索引迭代:__getitem__
迭代器物件:__iter__和__next__
所有迭代環境會先嚐試__iter__方法,再嘗試__getitem__方法。
當迭代器返回自身時,只支援一個活躍的迭代器。反之,則可支援獨立位置的多個活躍迭代器。
成員關係:__contains__、__iter__和__getitem__
優先順序如上所示
屬性引用:__getattr__和__setattr__
__getattr__攔截未定義的屬性
__repr__和__str__會返回字串表達形式
列印操作首先嚐試__str__和str內建函式
右側加法和原處加法:__radd__和__iadd__
只有+右側物件是類例項,左側不是時,才呼叫__radd__,其他所有情況下,由左側物件呼叫__add__方法。
Call表示式:__call__
當呼叫例項時,使用該方法。
比較:__It__、__gt__和其他方法
布林測試:__bool__和__len__
物件解構函式:__del__
類的設計
Python的OOP可概括三個概念:繼承、多型和封裝
應該吧程式程式碼寫成預期的物件介面,而不是特定的資料型別。
OOP和繼承:“是一個”關係
OOP和組合:“有一個”關係
OOP和委託:“包裝”物件
類的偽私有屬性
Class語句中開頭有兩個下劃線,但結尾沒有兩個下劃線的變數名自動擴張,_classname__x
方法是物件:繫結或無繫結
無繫結類方法物件:無self。通過對類進行點號運算從而獲取類的函式屬性。Python 3.0刪除了該概念。
繫結例項方法物件:self+函式對。通過對例項進行全運算從而獲取類的函式屬性
多重繼承:“混合”類
超類多於一個時,即為多重繼承
傳統類中,屬性搜尋深度優先
新式類(以及Python 3.0的所有類),屬性搜尋沿著樹層次,廣度優先
用__dict__列出例項屬性
用dir列出繼承的屬性
類的高階主題
擴充套件內建型別
通過嵌入擴充套件型別
通過子類擴充套件型別
新式類
(Python 3.0)所有類的繼承自object,所有物件都是object的例項
新式類變化
類和型別合併
繼承搜尋順序
針對內建函式的屬性獲取
新的高階工具
__slots__:只有其列表中的變數名可賦值為例項屬性
異常和工具
異常基礎
異常編碼細節
try/except/else語句
try下的程式碼代表主要動作,except定義異常的處理器,else子句提供無異常時的處理器
空的except子句捕捉一切異常,捕捉Exception的異常類似於此,但會忽略和系統退出有關的異常。
一旦捕捉了錯誤,控制權會在捕捉的地方繼續下去,即try語句之下,沒有直接的方式可以回到異常發生的地方。
try/finally語句
當控制權離開try程式碼塊時,Python先執行finally程式碼塊,然後才跳出。finally不會終止異常。
統一try/except/finally語句
raise語句
raise <instance>
raise <class>#Make and raise instance of class
raise 重新引發最近引發的異常,以傳播已經捕獲的異常。
Python 3.0異常鏈:raise from
assert語句(斷言)
with/as環境管理器
with expression [as variable]: with-block
expression返回一個支援環境管理器協議(有__enter__和__exit__方法)的物件。其__enter__方法的返回值會賦值給variable
若有異常被引發,__exit__(type,value,traceback)方法被呼叫,若其返回值為假,異常會重新引發,以傳播到with語句外。
沒有異常時,__exit__(None,None,None)會被呼叫。
異常物件
基於類的異常的特點:提供型別分類;附件狀態資訊;支援繼承。
異常的設計
巢狀異常處理器
關於sys.exc_info
有處理器處理時,返回(type、value和traceback)元組