《Python學習手冊》讀書筆記


  之前為了編寫一個svm分詞的程序而簡單學了下Python,覺得Python很好用,想深入並系統學習一下,了解一些機制,因此開始閱讀《Python學習手冊(第三版)》。如果只是想快速入門,我在這里推薦了幾篇文章,有其他語言編程經驗的人簡單看一看就可以很快地開始編寫Python程序了。

  黑體表示章節下划線表示可以直接在原文對應位置查到的專有技術名詞。

  原書配套答案請到http://www.hzbook.com/Books/4572.html下載,簡單注冊即可。

第三章 如何運行程序

  import進行模塊導入只能運行一次,多次運行需使用reload。

  模塊往往是變量名的封裝,被認為是命名空間。例如:

#myfile.py
title = "test"

>>>import myfile
>>>print myfile.title
test 

  替代方案是from,下面有同樣的效果:

>>>from myfile import title
>>>print tittle
test
from myfile import * 則可以把myfile所有變量全部導入(第19章內容)。

第四章 介紹Python對象類型

  雖然字符串支持多種操作,但是它具有不可變性,即原字符串不能改變,只能用新字符串作為結果賦予一個變量。下面是一個試圖改變原字符串的操作及報錯信息:

>>> s="spam"
>>> s[0] = 'z'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

 第五章 數字

   str和repr顯示格式

>>>num = 1/3.0
>>>num
0.33333333333333331
>>>print num
333333333333

>>>repr(num)
'0.33333333333333331' #交互模式回顯
>>>str(num)
'333333333333' #打印語句

 

  浮點數運算在精確方面有缺陷。這和硬件有關,打印結果也不能完全解決。

>>> 0.1+0.1+0.1-0.3
5.551115123125783e-17
>>> print 0.1+0.1+0.1-0.3
5.55111512313e-17 

  使用小數對象可以進行修正

>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3')
Decimal('0.0') 

 第六章 動態類型簡介

  a = 3這個語句實際上執行了三個步驟:創建一個對象代表值3;如果還未創建,創建一個變量a;將變量與新的對象3連接。這時,變量a成為了對象3的一個引用,也可以看做是指針。

  類型屬於對象,而不是變量,這就很好理解為什么Python中同一個變量名可以作為不同類型的對象的引用了。

  在這種機制下,每個對象都有一個引用計數器,當計數器為0時就被系統回收,這便是Python中對象的垃圾收集的方法了。

  不同變量引用同一個數字或字符串時,對變量操作(eg.a=3 a=a+2)只是創建了一個新的對象並使它引用新對象,這也是上一章提到的字符串不能改動的原因。而對於一些類型來說,有的操作確實能改變對象,如下所示:

#situation 1
>>>L1=[2,3,4]
>>>L2=L1
>>>L1=24
>>>L2
[2,3,4]

#situation 2
>>L1=[2,3,4]
>>>L2=L1
>>>L2[0]=24
>>>L1
[24,3,4]

  為了讓兩個變量使用不同的對象,可以拷貝對象,使用L2=L1[:]來代替L2=L1即可。對於字典則使用D.copy()方法。標准庫的copy模塊提供了一個對任意對象的調用方法,下面兩種方式的區別暫不討論:

import copy
X = copy.copy(Y) #表層拷貝
X = copy.deepcopy(Y) #深拷貝

  這里就出現了個問題,兩個引用是否是同一對象?可以用下面的方式判斷:

>>>L=[1,2,3]
>>>M=L
>>>L== M
True
>>>L is M
True

  負值索引相當於從末尾倒數。-1就是最后一個元素的索引。對於s="spam",-5是個非法的索引值。分號:前的空值表示從第一個開始,后的空值表示直到最后一個。

 第七章 字符串

  單雙引號是一樣的,這樣允許用戶不使用轉移字符來實現帶有單或雙引號的字符串。個人認為避免了按shift才能使用雙引號“的麻煩。

>>>'knight"s ',"knight's"
('knight"s ',"knight's")

  此外,合並相鄰的字符串常量,如'knight"s ' "knight's"(中間有空格)會顯示為'knight"s knight\'s'。可見,最外層是單引號,為了保持原內容,Python把單引號里的單引號改寫成了轉義字符,這個例子和書上的不同,更有助於理解。轉義字符和C很類似,多了幾種;但是Python里沒有空字符串,Python為每個字符串保存了內容和長度。同時,如果一個字符串中沒有合法的轉義編碼出現在"\"后,那么它將在字符串中保留反斜線。抑制轉義的方法是在字符串前加r,如r"C:\new\text.dat",此時的\n和\t就不會被當做是轉義字符,同時,這樣做也不必把\改寫成\\。

  三重引號適用於多行的字符串的直接輸入而不使用轉義字符。利用三重引號也可以實現類似C中/* */注釋掉代碼的目的。

  Unicode字符串通過在前面加u獲得。

  擴展分片是第三個索引,用作步進。這時完整的分片形式為X[I:J:K],其中步進為K。當步進取-1時,可以把字符串反轉,很神奇的方法。

  利用分片,可以對字符串進行修改,即把新字符串加到原字符串上,再把原字符串切掉。

  字符串格式化的用法與C的printf很像,不同之處在於所有參數外需要加一個(),形成%(arg1,arg2,arg3)的形式。格式化代碼請參考原書表格,通用結構:%[(name)][flags][width][.precision]code,其中name可以是字典名,這時在參數表里提供這個字典的鍵即可。

  既然字符串是對象,那么它就有對應的方法。書上介紹了修改字符串的replace()、查找find()、把每個元素取出創建列表的list()、把列表合並成字符串的join()(可以作為list()的反操作)、提取組件的split()。

 

第八章 列表

  用中括號表示列表,列表的組成對象是有序的,組成列表的各個對象允許不同。

  用大括號表示字典,字典的組成對象是無序的,字典鍵的搜索方式是哈希搜索,速度很快。

  可以用字典來模擬列表:使用序數作為字典的索引即可。類似地,字典可以用來表示一些稀疏矩陣。

  字典的get方法用於避免不存在的鍵,如果鍵不存在,返回值是0。

  字典接口是使用方式類似字典並且實際工作都和字典一樣的一些Python擴展程序的接口。

 

第9章 元組、文件及其他 

  用小括號表示元組,元組不能原處修改。

  為了避免只含一個元素的元組被當做表達式,使用一個逗號,寫為(40,)。逗號可以幫助識別元組,下面的也是元組的表示方式:

t = 0,'Ni',1.2,3

  從文件中讀取的是字符串,需要作為其他類型來操作時必須轉換。

  eval()用來把字符串作為對象,因此也可以達到執行Python的任何表達式。

  pickle模塊可以直接在文件中存儲幾乎任何Python對象。

  struct模塊提供了二進制數據的打包。打包+存入文件,讀取文件+解包。

  在“賦值VS引用”這一節,對於復合方式的賦值,修改其成員會導致所有使用該成員的對象的改變。直觀來看就是下面:

>>>X = [1,2,3]
>>>L = ['a', X, 'b']
>>>D = {'x':X, 'y':2}
>>>X[1] = 'surprise'
>>>L
['a', [1,'surprise',3], 'b']
>>>D
{'x':[1,'surprise',3], 'y':2}

  這是一個陷阱,為了避免這種情況,根據具體類型使用拷貝(比如分片、copy方法)而不是引用。

  Python內部暫時存儲並重復使用短字符串,因此對同樣內容的字符串,is判定可能根據其長度為True(字符串較短時)或False(字符串較長時)。不同類型的比較(用==進行)的判定方式不一樣。

  還有其他內置類型陷阱,如重復能增加層次深度,循環數據結構L=L.append(L)

 

第二部分練習題

  2.(摘自附錄B)分片運算超出邊界(例如,L[-1000:100])可工作,因為Python會縮放超出邊界的分片(必要時,限制值可設為零和序列長度)。以翻轉的方式提取序列是行不通的(較低邊界值比較高邊界值更大,例如,L[3:1])。你會得到空分片([ ]),因為Python會縮放分片限制值,以確定較低邊界永遠比較高邊界小或相等(例如,L[3:1]會縮放成L[3:3],空的插入點是在偏移值3處)。Python分片一定是從左至右抽取,即使你用負號索引值也是這樣(會先加上序列長度轉換成正值)。注意到,Python 2.3的第三限制值分片會稍微修改此行為:L[3:1:-1]的確是從右至左抽取。

  3.索引運算、分片運算以及del:對於L=[1,2,3,4], L[2] = []只能把3變為[],而L[2:3] = []卻能刪掉第三項。

  4.X,Y = Y,X,左邊視為兩個對象,右邊視為一個元組,這個表達式交換了兩個引用。

 

第10章 Python語句簡介 

  絕大多數的Python程序每行一個語句,不需要分號。Python的風格就是完全不要分號,雖然在語句末加上分號也能通過。唯一需要分號的情況是一行中多個語句的分隔符。相反地,括號可以使一個語句分隔成很多行,比如用列表直觀地定義一個矩陣時。反斜線\也可以達到這個目的。

  嵌套代碼只需要保持縮進一致即可。Python是WYSIWYG語言(what you see is what you get,所見即所得)

 

第11章 賦值、表達式和打印

  形如X+=Y的賦值語句稱為增強賦值語句,它有三個優點:程序員輸入減少,左側只需要計算一次(X=X+Y中X計算兩次),優化技術會自動選擇(支持原處修改的類型可以直接原處修改)。

  單一下划線開頭的變量名不會被from module import *這樣的語句導入。前后都有雙下划線的變量名是系統定義的變量名,對解釋器有特殊意義。雙下划線開頭但結尾沒有雙下划線的變量是類的本地變量(參考第19章)。

  表達式語句通常用於執行可原處修改列表的列表方法,即對於列表L,L.append(3)是正確的,而L=L.append(4)是錯誤的,第二個式子右邊返回的是None。

  標准輸出的重定向方法:

import sys
sys.stdout = open('log.txt','a')
...
print x,y,x #寫入log.txt

 為了避免忘記恢復sys.stdout,寫入log.txt也可以用:

log = open('log.txt','a')
print >>log, x, y, x

 

第12章 if測試

   類似於C,Python的布爾運算or是短路運算,而它返回第一個為真的操作對象,或者是第二個為假的對象。[ ] or { } 將返回{ }。

  if選擇分支有以下幾種等價形式:

#最常見的分支形式
if X:
    A = Y
else:
    A = Z

#Python2.5以后引入
A = Y if X else Z

#Python2.5以前(以后也兼容)
#需要理解and和or的運算和返回值規則
A = ((X and Y) or Z)

#列表形式
A = [Z,Y][bool(X)]

 

第13章  while和for循環

  pass語句是無運算的占位符,為了表示語法需要語句並且還沒有任何實用的語句可寫時就可以使用它。

  while和for循環都有一個可選的else語句,在循環條件不滿足時且沒有用break結束循環時使用。

  C語言形式的 while((x = next()) != NULL) { ...process x...}在Python里行不通:C語言賦值語句會返回賦值后的值,而Python賦值語句只是語句,不是表達式。

  Python的迭代協議:有next方法的對象會前進到下一個結果,而在末尾時引發StopIteration。所有的迭代工具內部工作都調用next,並捕捉StopIteration異常來確定何時離開。

  用for修改列表時,for x in L:x+=1是無法修改的,因為它修改的是循環變量x而不是列表L。應該使用L[i]  +=1的索引來控制修改。

  zip()可以用於for並行修改多個對象時的情況(按最短的截斷)。它也可以用來再循環中建立列表:for (k,v) in zip(keys, vals):D[k]=v。

  enumerate()用於產生偏移和元素:for (offset,item) in enumerate(S): print offset,item

  基本的列表解析:L=[x+10 for x in L]

  擴展的列表解析,刪除文件中的換行符:

lines = [line.rstrip() for line in open('script1.py') if line[0] =='p']

  從文件中逐行讀取文本行的最佳方法是不要刻意去讀:

for line in open('script1.py'):
    print line.upper()

 

第十四章 文檔

  __doc__屬性封裝了對象上的文檔,通過它可以查看(比如函數的)注釋。

  文檔字符串被認為最是用於較大、功能性的文檔,而#最好只限於關於費解的表達式或語句的微型文檔。PyDoc系統能夠將前者取出並顯示。

 

第十五章 函數基礎

  def是可執行代碼,直到運行了def時其定義的函數才開始存在。

  由於函數的參數沒有類型規定,因此可以很方便地實現多態:

>>>def times(x,y):
...        return x*y
...

>>>times(2,4)
8
>>>times('Ni',4)
'NiNiNiNi'

 

第十六章 作用域與參數

  變量名解析的LEGB原則:變量名引用分為三個作用域進行查找,本地作用域(L,每次調用函數時創建)、上一級調用的本地作用域(E)、全局作用域(G,模塊作用域)、內置作用域(B,預定義的變量名如open)。僅對簡單變量生效,對於特定對象的變量如object.spam,查找規則規則完全不同。

  內置作用域是一個名為__builtin__的內置模塊,import后才可以使用,這時可以用dir(__buildin__)查看預定義的變量名。根據LEGB原則,在本地作用域定義一個新的open = 'spam'會導致open()函數不能被調用。

  global用於全局變量聲明。

  作者認為在模塊導入時,導入其他模塊的模塊擁有了對其他模塊變量的修改權,這使得被導入模塊的維護變得復雜:維護者不知道第二個模塊什么情況下會修改變量。因此最好的解決辦法是不這樣做,在文件間進行通信的最好辦法就是通過調用函數,傳遞參數,然后獲得返回值(用函數提供修改變量的接口,並且告訴維護者這個變量可以被其他模塊改變)。

  工廠函數(又稱閉合),是能記住嵌套作用域的變量值的函數。示例:

>>> def maker(N):
...     def action(X):
...             return X ** N
...     return action
... 
>>> f = maker(2)
>>> f #顯示f的引用
<function action at 0xb7738294>
>>> f(3)
9
>>> f(4)
16
>>> g = maker(3)
>>> g(3)
27
>>> f(3)
9

  對於函數參數,不可變參數是通過傳值來傳遞,可變對象(列表、字典等)是通過傳引用進行傳遞的。

  多個返回值的常用return方式是使用元組。

  函數參數的四種匹配方式:位置匹配、關鍵字參數、所有對象、所有關鍵字/值:

>>>def f(a,b,c):print a,b,c #常規匹配
>>>f(1,2,3)
1 2 3

>>>f(c=3,b=2,a=1) #關鍵字匹配
1 2 3

>>>f(1,c=3,b=2) #位置+關鍵字匹配
1 2 3

>>>def f(a,b=2,c=3):print a,b,c #默認參數
>>>f(1)
1 2 3
>>>f(a=1)
1 2 3

>>>def f(*args):print args # 任意參數*
>>>f()
()
>>>f(1)
(1,)
>>>f(1,2,3,4)

>>>def f(**args):print args # 任意參數**,把參數組裝成為字典
>>>f()
{}
>>>f(a=1,b=2)
{'a'=1,'b'=2}

相反地,調用函數在參數變量前面加*可以分解參數。

  參數匹配順序:調用和定義中先是非關鍵字參數(name)、然后是關鍵字參數(name=value)、*name最后是**name。

 

第17章 函數的高級話題

  def f(x,y,z):return x+y+z和f= lambda x,y,z:x+y+z會達到同樣的效果。lambda是一個表達式,而不是語句,允許出現在def不能出現的地方。正是因為這個特點,lambda比def的使用更加靈活,比如編寫跳轉表(也即行為的列表或字典):L=[(lambda x:x**2),[(lambda x:x**3),[(lambda x:x**4)]。出於代碼易讀性的考慮,應盡量避免嵌套的lambda。

  apply的介紹略過,它可以用*和**型參數代替。(似乎在Python3.0以上版本已廢棄,待確認)

  map(func,arg)可以很方便的用函數func處理列表類型的數據,而自己編寫類似的功能需要使用for來完成。

  filter和reduce這兩個函數工具分別用於列表過濾和列表全元素的逐個運算。

  關於列表解析,帶if條件的之前已提過,不再重復。for的應用示例:

>>> res = [x+y for x in [0,1,2] for y in [100,200,300]]
>>> res
[100, 200, 300, 101, 201, 301, 102, 202, 302]

更進一步的嵌套:

>>>[(x,y) for x in range(5) if x%2 ==0 for y in range(5) if y%2==0]

作者在這里開了個小玩笑:“而map和filter的等效形式往往更復雜也會有深層的嵌套,這里不進行說明,將這部分代碼留給禪師、前LISP程序員以及犯罪神經病作為練習”。

  生成器函數與一般函數不同之處在於,它yield而不是return一個值,並把自己掛起,現場保存在下一次調用。為與列表解析相區分,可以使用圓括號作為生成器表達式:

>>> for num in (x **2 for x in range(4)):
...     print '%s,%s' %(num,num/2.0)
... 
0,0.0
1,0.5
4,2.0
9,4.5

  一個測試不同的迭代方法的小程序。當然,對於不同的操作,不同方法的相對速度可能不一樣,不存在所有情況下都最快的“最優方法”:

#file timerseqs.py
import time,sys
reps = 1000
size = 10000

def tester(func, *args):
    startTime = time.time()
    for i in range(reps):
        func(*args)
    elapsed = time.time() - startTime
    return elapsed

def forStatement():
    res = []
    for x in range(size):
        res.append(abs(x))

def listComprehension():
    res = [abs(x) for x in range(size)]

def mapFunction():
    res = map(abs, range(size))

def generatorExpression():
    res = list(abs(x) for x in range(size))

print sys.version
tests = (forStatement, listComprehension, mapFunction,generatorExpression)
for testfunc in tests:
    print testfunc.__name__.ljust(20), '=>',tester(testfunc)

  陷阱:本地變量是靜態檢測的。這意味着如果在模塊里定義了X=99,def一個函數print X后又在函數里X=88,那么就會報錯。

  陷阱:默認對象在def時賦值,而不是調用函數時賦值。

 

第18章 模塊:宏偉藍圖

  Python進行import時搜索目錄的順序:主目錄、PYTHONPATH環境變量目錄、標准庫目錄、.pth目錄。

 

第19章 模塊代碼編寫基礎 

  將會被用於導入的模塊文件命名需要以.py做結尾。

  當兩個不同模塊使用了相同的變量名時,不能用from,只能用import。

  (本章大部分內容都在第三章介紹過)

 

第20章 模塊包

  import時列出路徑名稱,以點號相隔:import dir1.dir2.mod。這與平台無關,import不能使用平台特定的路徑表達方式。同時,這也表明文件名省略了.py的原因。另外,dir1和dir2中必須包含一個__init__.py文件(可以為空,Python首次進入其所在目錄時會執行它的內容)。每次使用路徑必須完整輸入,使用import dir1.dir2.mod as mod中定義的mod代替前面過長的路徑名可以解決這個問題。

  個人認為,模塊包是為了方便同名模塊的使用不發生混淆的方式,這是軟件開發時所需要的。

 

第21章 高級模塊話題

  _X的命名方式可以防止from *導入這個變量,然而這種方法不能阻止其他導入方式的導入,並不是一些面向對象語言中的私有聲明。

  __all__會列出from *復制的變量名,與_X正相反。同樣只對from *有效,不是私有聲明。

  from __feature__ import featurename還不是很理解,好象是用選用擴展功能的方式開啟特殊的代碼編譯。

  模塊可以通過檢測自己的__name__是否為"__main__"確定它是在執行還是被導入。這樣可以讓模塊在扮演兩種不同角色時發揮不同功能。

  相對導入:路徑以一個點開始,定位同一個包的模塊。可以開啟__feature__中強迫導入的絕對性。很類似於Linux,兩個點表示上一級路徑。

  陷阱一:頂層代碼的語句次序。被import時模塊的頂層代碼會立即執行,此時它所引用后文定義的變量將無效。

  陷阱二:字符串變量是不能直接用於import語句的。可以使用exec "import" + modname來使用字符串modname。這樣做仍然有個缺點,每次執行時必須編譯import語句。更好的代替方案是string = __import__(modname),然后把string單列一行執行即可。

  陷阱三:from復制變量名而不是拷貝。

#nested1.py
X = 99
def printer():print X

#nested2.py
from nested1 import X,printer
X = 88
printer()

%python nested2.py
99

  陷阱四:reload不影響from導入。為了更新變量,使用.運算符來導入和修改其他模塊的變量。

  陷阱五:reload、from及交互模式測試。這部分比較有啟發性,建議在原書仔細閱讀,簡要概括就還是:導入后(模塊)要重載(模塊),重載后還要重新執行import(變量)。reload和from的合作並不完美,最佳原則是使用reload和import來啟動程序。

  陷阱六:重載沒有傳遞性。重載A不會重載A中import的B和C。需要這種功能時可以自己編寫一個通用工具。

reload_all
import types
def status(module):
    print 'reloading',module.__name__

def transitive_reload(module,visited):
    if not visited.has_key(module):
        status(module)
        reload(module)
        visited[module] = None
        for attrobj in module.__dict__.values(): #For all attrs
            if type(sttrobj) == types.ModuleType:
                transitive_reload(attrobj,visited)

def reload_all(*args):
    visited = {}
    for arg in args:
        if type(arg) == types.Module Type:
            transitive_reload(arg,visited)

if __name__ == '__main__':
    import reloadall
    reload_all(reloadall)

  陷阱七:遞歸導入。

 

第22章 OOP:宏偉藍圖

  屬性通常是在class語句中通過賦值語句添加在類中,而不是在定義類時嵌入。因此對沒有賦值的對象屬性的訪問會出錯。

  類方法函數第一個參數通常為self(調用時不指明),但不一定叫self,位置是關鍵(來自習題5)。作為類方法直接調用時,需指明實例的名稱(24章)。

  Python的OOP模型其實就是在對象樹中搜索屬性。

  (筆者有部分OOP基礎,因此本章具體理論和理解略去)

 

第23章 類代碼編寫基礎

  類其實也是一種對象。

  在類定義外創建的函數也可以成為方法:

 

>>>def upperName(self):
...        return self.name.upper()

>>>rec.method = upperName

 

 

第24章 類代碼編寫細節

  和def一樣,class也是可執行代碼,運行時才會產生類對象。

  調用超類的構造器是可以的,在子類的構造方法中使用Super.__init__()即可。

  抽象超類有的方法沒有提供實現,而是由子類提供。

  類的運算符重載通過修改諸如__add__(對應於+)等方法來實現。具體細節請參考原書。下面是一個修改__iter__獲得用戶定義的迭代器的例子:

View Code
class Squares:
    def __init__(self,start,stop):
        self.value = start - 1
        self.stop = stop
    def __iter__(self):
        return self
     def next(self):
        if self.value == self.stop:
            raise StopIteration
        self.value += 1
        return self.value ** 2

%python
>>>from iters import Squares
>>>for i in Squares(1,5):
...        print i,
...
1 4 9 16 25

   __setattr__的修改可以模擬實現成員變量私有性,這里不貼書中的源碼了。

  右側方法如__radd__中,self在右側,和__add__相反。

  __call__可以攔截調用,用使用函數的方法使用類。對改寫了__call__的類prod,實例化x = prod(2),x(3)可以直接使用。

  __del__是析構器,但在Python中很少使用析構方法。

  命名空間其實是普通的字典。

  

第25章 類的設計

  無綁定類方法對象無self必須明確提供實例對象做第一個參數,綁定實例方法對象用self+函數對,不用傳遞實例。

  委托是指把對象包裝在代理類中。

#trace.py
class wrapper:
    def __init__(self,object):
        self.wrapped = object
    def __getattr__(self,attrname):
        print 'Trace:',attrname
        return getattr(self.wrapped,attrname)

>>>from trace import wrapper
>>>x = wrapper([1,2,3])
>>>x.append(4)
Trace:append
>>>x.wrapped
[1,2,3,4]

  組合是一種技術,讓控制器類嵌入和引導一群對象,並自行提供接口。

  (這一章主要內容是幫助讀者從面向過程向面向對象過渡,而且比較淺顯,在這方面作者推薦繼續去讀設計模式的書效果會更好,這里就不詳細介紹了)

 

 第26章 類的高級主題

  偽私有屬性:將開頭兩個下划線的變量名前再加上_類名。仍然不是真正的私有。

  新式類從內置類型創建子類,或者直接用object作為超類。3.0以后所有類自動成為新式類。鑽石繼承在新式類里從括號最右開始搜索,這與經典類正相反。為了解決繼承不同類同名變量沖突,可以進行強制規定,如attr = B.attr。

  __slots__用來限制類的實例能有的合法屬性集。

  內容屬性使用攔截的方式來提供屬性,但是它本身不是成員變量。類似於改寫__getattr__,使用property()進行。

  靜態方法類方法分別需要調用staticmethod和classmethod兩個函數,前者調用不需要實例(實例調用時),后者把類傳入類方法第一個參數。

  函數裝飾器在def上一行用@標明,有點像包裹函數,@A @B @C后def f()相當於f=A(B(C(f)))。

 

第27章 異常基礎

  try/except可以用於捕捉異常並從異常中恢復,而try/final可以保證無論是否發生異常,終止行為都一定會進行。二者也可以合並使用(2.5版以后)。else在不發生異常時執行。except有幾種分句形式(請參考原書)。

  rasie、assert用於觸發異常。raise后不帶參數表示重新引發當前異常(第28章)。

  with/as可以用作try/final的替代方案。as后面是with后表達式的賦值對象。

 

第28章 異常對象

  字符串異常(myexc = "My exception string";raise myexc)已經在3.0以后消失,現在常用的是基於類的異常。類異常比字符串異常方便之處在於,可以在原始版本中用超類定義異常,在后續版本中使用子類來描述新的異常,這為版本維護提供了極大的方便。字符串異常的判斷方式是is而不是==(常見陷阱,29章)。

 

第29章 異常的設計

  嵌套的try,引發異常時except會回到先前進入但未離開的try,而finally不會停止傳遞。

  用try進行調試的方式,在錯誤發生時程序仍處於激活狀態,可以進行其他的測試而不是重新開始:

try:
    ...run program...
except:
    import sys
    print 'uncaught!', sys.exc_info()[0], sys.exc_info()[1] 
  #sys.exc_info有專門一小節講解,無異常返回3個None
  #反之返回type value tracebck

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM