import this 會觸發python內部的一個彩蛋,它將顯示python語言層面之下的設計哲學。
Industrial Light & Magic、Pixar用python製作動畫電影。
python已經證明它是無所不能的。
python的缺點是它的性能:它不像C和C++這類常規的編譯語言運行得那麼快。
python的應用領域:
- 系統編程
- 用戶圖形接口
- Internet腳本
- 組件集成
- 數據庫編程
- 快速原型
- 數值計算和科學計算編程
- 遊戲、圖像、人工智能、XML、機器人等
python解釋器:
解釋器是一種讓其他程序運行起來的程序。python也是一個名爲解釋器的軟件包。解釋器是代碼與機器的計算機硬件之間的軟件邏輯層。
接下去開始編寫程序:
- 每一個以擴展名.py結尾的Python源代碼文件都是一個模塊,其他的文件可以通過導入一個模塊讀取這個模塊的內容。導入從本質上來講,就是載入另一個文件,並能夠讀取那個文件的內容。這種基於模塊的方式使模塊變成了python程序架構的一個核心概念。更大的程序往往以多個模塊文件的形式出現,並且導入了其他模塊文件的工具。其中的而一個模塊文件被設計成主文件,或叫頂層文件。
- 導入是一個開銷很大的操作以致於每個程序運行不能夠重複多於一次。但是如果真的想要python在同一次會話中再次運行文件(不停止和重新啓動會話),需要調用內置的reload函數。
- 模塊的顯要特性:屬性。
- 模塊和命名空間:模塊將變量封裝爲不同部分,python具有了能夠避免命名衝突的優點。模塊是一個不需要重複輸入而可以反覆運行代碼的方法。
- IDLE沒有清屏選項。
第二部分:類型和運算
python對象類型:數據以對象的形式出現。
- 程序由模塊構成
- 模塊包含語句
- 語句包含表達式
爲什麼使用內置類型:
python提供了強大的對象類型作爲語言的zuchengbuf,在你開始解決問題之前往往沒有必要編寫對象的實現。
- 內置對象使程序更容易編寫。(列表、字典等)
- 內置對象是擴展的組件。
- 內置對象往往比定製的數據結構更有效率。
- 內置對象是語言的標準的一部分。
python的核心數據類型:
- 數字
- 字符串 'spam' , "guido's"
- 列表 [1,[2, 'three'], 4]
- 字典 {'food': 'spam', 'taste': 'yum'}
- 元組 (1, 'spam', 4, 'U')
- 文件 myfile = open('eggs', 'r')
- 其他類型 集合、類型、None、布爾型
python中沒有類型申明。
一旦創建了一個對象,它就和操作集合綁定了。
python是動態類型的(它自動地跟蹤你的類型而不是要求聲明代碼),但是它也是強類型語言(你只能對一個對象進行有效的操作)。
字符串:
- 序列的操作:一個負的索引號簡單地與字符串的長度相加,得到兩個操作是等效的。除了簡單的從位置進行索引,序列也支持一種所謂的分片(slice)的操作,這是一種一步就能夠提取整個分片的方法。X[ I : J ]。表示“取出在X中從偏移爲I,直到但不包括J的內容”。在一個分片中,左邊界默認爲0,並且右邊界默認爲分片序列的長度。負偏移量也可以作爲分片的邊界。作爲一個序列,字符串也支持使用加號進行合併,或者重複(用*)。
- 不可變性:字符串在python中具有不可變性——在其創建後值不能改變。例如:你不能通過對其某一位置進行賦值而改變字符串。
動態類型一集由它提供的多態性,這個概念無疑是python語言的簡潔性和靈活性的基礎。
變量、對象和引用:
類型的概念是存在於對象中而不是變量名中。例如:a = 3
python語言中所有賦值的操作:
- 創建一個對象來代表值3。
- 創建一個變量a,如果它還沒有創建的話。
- 將變量與新的對象3相連接。
- 變量是一個系統表的元素,擁有指向對象的連接的空間。
- 對象是被分配的一塊內存,有足夠的空間去表現它們所代表的值。
- 引用是自動形成的從變量到對象的指針。
例如我們只是把a引用了不同的對象。因爲變量沒有類型,我們實際上並沒有改變變量a的類型,只是讓變量引用了不同類型的對象而已。實際上,python的變量就是在特定的時間引用了一個特定的對象。
另一方面,對象知道自己的類型。每個對象都包含了一個頭部信息,其中標記了這個對象的類型。因此,python中的類型是與對象相關聯的,而不是和變量關聯。
對象的垃圾收集:
在內部,python是通過保持用每個對象中的計數器記錄引用指到這個對象上的次數來完成這一功能的。一旦這個計數器被設置成零,這個對象的內存空間就會自動回收。
共享引用和在原處修改:
有一些對象和類型確實會在實地改變對象。
python拷貝對象,而不是創建引用。拷貝列表的方法:如從頭到尾的分片。
L1 = [2, 3, 4]
L2=L1[ : ] # make a copy of L1, L2 is not changed.
L1[0] = 24
對字典應該使用D.copy()方法。注意標準庫中的copy模塊有一個通用的拷貝任意對象類型的調用,也有一個拷貝嵌套對象結構的調用。
共享引用和相等:
“==操作符” 測試兩個被引用的對象是否有相同的值;
“is操作符 檢查對象的同一性,如果兩個變量名精確地指向同一對象,它會返回TRUE,所以這是一種更嚴格形式的相等測試。
第七章 字符串:單引號、雙引號、轉義字符、raw字符串、塊字符串、unicode字符串
在python字符串中,單引號和雙引號字符是可以互換的。
如果字母r(大寫或小寫)出現在字符串的第一引號的前面,它將會關閉轉義機制。這個結果就是python會將反斜線作爲常量保持,就像輸入的那樣。
三重引號字符串常用於文檔字符串;(另外,三重引號字符串經常在開發過程中作爲一種恐怖的黑客風格的方法去廢除一些代碼。)
索引和分片:
分片表達式能夠返回除了第一項之外的所有元素的列表;
分片也常常用作清理輸入文件的內容。如:line[ : -1]把這行除去最後一個字符之外的所有內容提取出來。(爲了去掉換行符常常推薦line.rstrip()方法,因爲這個調用將會留下沒有換行字符那行的最後一個字符。)
字符串格式化:
%s : 字符串(或任何對象)
%r : s,但使用repr,而不是str
%c : 字符
%d : 十進制整數
%i : 整數
%u : 無符號整數
%o : 八進制整數
%x : 十六進制整數
%X : x,但打印大寫
%e : 浮點指數
%E : e,但打印大寫
%f : 浮點十進制
%g : 浮點e或f
%G : 浮點E或f
%% : 常量%
序列:支持索引、分片、合併;
映射:支持通過鍵的索引。
可變類型能夠在原處修改。
第八章:列表與字典
這兩種類型相當靈活,它們可以在原處修改,也可以按需求增長或縮短,而且可以包含任何種類的對象或者被嵌套。
列表:
列表是python中最具靈活性的有序集合對象類型。可以包含任何種類的對象:數字、字符串甚至其他列表。
任意對象的有序集合;
通過偏移讀取;
可變長度、異構以及任意嵌套;
屬於可變序列的分類;
對象引用數組;
對列表進行索引的結果就是你指定的偏移處的對象(不管是什麼類型),而對列表進行分片時往往返回一個新的列表。
索引和分片的賦值都是原地修改,它們對列表進行直接修改,而不是生成一個新的列表作爲結果。
字典:
字典是python中最具靈活的內置數據結構類型。如果把列表看作是有序的對象集合,那麼就可以把字典當成是無序的集合。它們主要的差別在於:字典當中的元素是通過鍵來存取的,而不是通過偏移存取。
作爲內置類型,字典可以取代許多搜索算法和數據結構。
主要屬性如下:
通過鍵而不是偏移量來讀取;
任意對象的無序集合;
可變長、異構、任意嵌套;
屬於可變映射類型;
對象引用表(哈希表);
與列表不同的是,當對新字典鍵進行賦值(之前沒有被賦值的鍵),就會再字典內生成一個新的元素;在列表中情況不同,因爲python會將超出列表末尾的偏移視爲越界並報錯。要想擴展列表,你需要使用append方法或分片賦值來實現。
>>> d2.get('spam')
2
字典的update方法有點類似於合併,它把一個字典的鍵和值合併到另一個,盲目地覆蓋相同鍵的值。
>>> d2
{'eggs': 3, 'ham': 1, 'spam': 2}
>>> d3 = {'toast': 4, 'mu':5}
>>> d2.update(d3)
>>> d2
{'toast': 4, 'mu': 5, 'eggs': 3, 'ham': 1, 'spam': 2}
>>>
字典pop方法能夠從字典中刪除一個鍵並返回它的值。這類似於列表的pop方法,只不過刪除的是一個鍵而不是一個可選的位置。
>>> d2
{'toast': 4, 'mu': 5, 'eggs': 3, 'ham': 1, 'spam': 2}
>>> d2.pop('mu')
5
>>> d2
{'toast': 4, 'eggs': 3, 'ham': 1, 'spam': 2}
>>>
>>> # pop a list by position
>>> L = ['aa', 'bb', 'cc', 'dd']
>>> L.pop()
'dd'
>>> L
['aa', 'bb', 'cc']
>>> L.pop(1)
'bb'
>>> L
['aa', 'cc']
>>>
語言表:
>>> table ={'python' : 'Guido van Rossum',
'perl': 'Larry Wall',
'Tcl': 'John Ousterhout'}
>>> language = 'python'
>>> creator = table[language]
>>> creator
'Guido van Rossum'
>>> for lang in table.keys():
print(lang, '\t', table[lang])
python Guido van Rossum
Tcl John Ousterhout
perl Larry Wall
>>>
字典用法注意事項:
>>> {'name': 'mel', 'age': '45'}
{'age': '45', 'name': 'mel'}
>>> d ={}
>>> d['name'] = 'mel'
>>> d['age'] = '45'
>>> d
{'age': '45', 'name': 'mel'}
>>>
>>> dict(name = 'mel', age = '45')
{'age': '45', 'name': 'mel'}
>>>
>>> dict([('name', 'mel'), ('age', '45')])
{'age': '45', 'name': 'mel'}
>>>
這四種形式都會建立相同的兩鍵字典:
>>> dict.fromkeys(['a', 'b'], 0)
{'a': 0, 'b': 0}
>>>
第九章 元組(tuple)、文件及其他
元組:
python集合類型是元組(tuple)。元組由簡單的對象組構成,元組與列表非常類似,只不過元組不能在原處修改(它們是不可變的),並且通常寫成圓括號(而不是方括號)中的一系列項。元組不支持任何方法調用,但元組具有列表的大多數屬性。
任意對象的有序集合:與字符串和列表類似,元組是一個位置有序的對象的集合。
通過偏移存取:同字符串、列表一樣,支持所有基於偏移的操作:索引和分片。
屬於不可變序列類型:類似於字符串,不支持應用在任何原處修改操作。
固定長度、異構、任意嵌套:因爲元組是不可變的,在不生成一個拷貝的情況下不能增長或縮短。另一方面,元組可以包含其他的複合對象(例如:列表、字典、其他元組等),因此支持嵌套。
對象引用的數組:與列表相似,元組最好被認爲是對象引用的數組。
元組的特殊語法:逗號和圓括號
如果圓括號裏的單一對象是元組對象而不是一個簡單的表達式時,需要對python進行特別說明。如果確實想得到一個元組,只要在這個單個元素之後、關閉圓括號之前加一個逗號就可以了。
>>> x = (40)
>>> x
40
>>> x = (40,)
>>> x
(40,)
>>>
作爲特殊情況,在不引起語法衝突的情況下,python允許忽略元組的圓括號。僅當元組作爲文字傳給函數調用(圓括號很重要)以及當元組在print語句中列出(逗號很重要)的特殊情況時,圓括號纔是必不可少的。
轉換以及不可變性:
如果想對元組進行排序,通常先得將它轉換爲列表才能獲得使用排序方法調用的權限,並將它變爲一個可變對象。
>>> T = ('cc', 'aa', 'dd', 'bb')
>>> tmp = list(T)
>>> tmp.sort()
>>> tmp
['aa', 'bb', 'cc', 'dd']
>>> T = tuple(tmp)
>>> T
('aa', 'bb', 'cc', 'dd')
>>>
列表解析(list comprehension)也可以用於元組的轉換。
>>> T = (1, 2, 3, 4, 5)
>>> L = [x + 20 for x in T]
>>> L
[21, 22, 23, 24, 25]
>>>
列表解析是名副其實的序列操作——它們總會創建新的列表,但也可以用於遍歷包括元組、字符串以及其他列表在內的任何序列對象。列表解析甚至可以用在某些並非實際存儲的序列之上——任何可遍歷的對象都可以,包括可自動逐行讀取的文件。
注意元組的不可變性只適用於元組本身頂層而並非其內容。例如,元組內部的列表是可以像往常那樣修改的。
>>> T = (1, [2, 3, 4], 5)
>>> T[1] = 'spam'
Traceback (most recent call last):
File "<pyshell#61>", line 1, in <module>
T[1] = 'spam'
TypeError: 'tuple' object does not support item assignment
>>> T[1][1] = 'spam'
>>> T
(1, [2, 'spam', 4], 5)
>>>
爲什麼有了列表還要元組:
元組的不可變性提供了某種完整性。元組也可以用在列表無法使用的地方。例如,作爲字典鍵。
文件:
內置open函數會創建一個python文件對象,可以作爲計算機上的一個文件鏈接。文件對象不是數字、序列也不是映射。
記住:現在從文本文件讀取文字行的最佳方式是根本不要讀取該文件。文件也有個迭代器會自動地在for循環、列表解析或者其他迭代語句中對文件進行逐行讀取。
實際應用中的文件:
>>> myfile = open('myfile', 'w')
>>> myfile.write('hello text file\n')
16
>>> myfile.close()
>>> myfile = open('myfile')
>>> myfile.readline()
'hello text file\n'
>>> myfile.readline()
''
寫入方法不會爲我們添加行終止符,所以程序必須包含它來嚴格地終止行。
在文件中存儲並解析python對象:
>>> F = open('datafile.txt', 'w')
>>> F.write(S + '\n')
5
>>> F.write('%s,%s,%s\n' % (X, Y, Z))
9
>>> F.write(str(L) + '$' + str(D) + '\n')
27
>>> F.close()
>>> bytes = open('datafile.txt').read()
>>> bytes
"spam\n43,44,45\n[1, 2, 3]${'a': 1, 'b': 2}\n"
>>> print(bytes)
spam
43,44,45
[1, 2, 3]${'a': 1, 'b': 2}
>>> F = open('datafile.txt')
>>> line = F.readline()
>>> line
'spam\n'
>>> line.rstrip()
'spam'
>>>
>>> line = F.readline()
>>> line
'43,44,45\n'
>>> parts = line.split(',')
>>> parts
['43', '44', '45\n']
>>>
#int 能夠把數字字符串轉換爲整數對象,注意:我們不一定非要運行rstrip來刪除最後部分的“\n”,int 和一些其他轉換方法會忽略數字旁邊的空白。
['43', '44', '45\n']
>>> int(parts[1])
44
>>> number = [int(p) for p in parts]
>>> number
[43, 44, 45]
#轉換列表和字典
#使用eval可以把字符串轉換成對象
>>> parts = line.split('$')
>>> parts
['[1, 2, 3]', "{'a': 1, 'b': 2}\n"]
>>> eval(parts[0])
[1, 2, 3]
>>> objects = [eval(p) for p in parts]
>>> objects
[[1, 2, 3], {'a': 1, 'b': 2}]
>>>
用pickle存儲python的原生對象:
pickle模塊是能夠讓我們直接在文件中存儲幾乎任何python對象的高級工具,也並不要求我們把字符串轉換來轉換去。它就像是超級通用的數據格式化和解析工具。例如,想要在文件中存儲字典,就直接用pickle來存儲。
對象靈活性:
賦值操作總是存儲對象的引用,而不是這些對象的拷貝。
拷貝:
比較、相等性和真值
>>> L1 = [1, ('a', 3)]
>>> L2 = [1, ('a', 3)]
>>> L1 == L2, L1 is L2
(True, False)
L1,L2被賦值爲列表,雖然相等,卻是不同的對象。
>>> S2 = 'spam'
>>> S1 ==S2, S1 is S2
(True, True)
在這裏,兩個截然不同的對象碰巧有着相同的值:但是因爲在python內部暫時存儲並重複使用短字符串作爲最佳化,事實上內存裏只有一個字符串‘spam’供S1和S2分享。因此,“is”一致性測試結果爲真,爲了得到更一般的結果,我們需要使用更長的字符串:
>>> S1 = 'a longer string'
>>> S2 = 'a longer string'
>>> S1 == S2, S1 is S2
(True, False)
相對大小的比較也能夠遞歸地應用於嵌套的數據結構。
>>> L1 = [1, ('a', 3)]
>>> L2 = [1, ('a', 2)]
>>> L1 < L2, L1 == L2, L1> L2
(False, False, True)
一般來說,python中不同的類型的比較方法如下:
>>> L = [] * 100
>>> L
[]
>>> L = [None] * 100
>>> L
[None, None, None, ……]
對於列表來說,只能給已經存在的偏移賦值的。預先分配一個100項的列表,這樣你可以在100個偏移的任何一個加上None對象。
重複能夠增加層次深度:
序列重複就好像是多次將一個序列加到自己身上。
>>> L = [4,5,6]
>>> X = L * 4
>>> Y = [L] * 4
>>> X
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> Y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
>>> L[1] = 0
>>> X
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> Y
[[4, 0, 6], [4, 0, 6], [4, 0, 6], [4, 0, 6]]
重複、合併以及分片只是在複製操作數對象的頂層。
留意循環數據結構
>>> L = ['grail']
>>> L.append(L)
>>> L
['grail', [...]]
第三部分:語句和語法
第十章:python語句簡介:
python程序結構:
第十一章:賦值、表達式和打印:
賦值語句:
>>> L = [1, 2]
>>> M = L # L和M引用相同的對象
>>> L = L + [3, 4] # 合併生成一個新的對象,運行起來更慢一些
>>> L, M
([1, 2, 3, 4], [1, 2])
>>> L = [1, 2]
>>> M = L
>>> L += [3, 4] # 但是增強賦值+=真正意味着擴展
>>> L,M # M原地修改
([1, 2, 3, 4], [1, 2, 3, 4])
變量命名規則:
1、語法:(下劃線或字母)+ (任意數目的字母、數字或下劃線)
變量名必須以下劃線或字母開頭,而後面接任意數目的字母、數字或下劃線。
2、區分大小寫。
3、禁止使用保留字。python保留字都是小寫的,無法對保留字做賦值運算。
命名慣例:
變量名沒有類型,但對象有。
表達式語句和在原處的修改
>>> L = [1, 2]
>>> L.append(3)
>>> L
[1, 2, 3]
>>> L = [1, 2]
>>> L = L.append(3)
>>> L
>>>
>>> print(L)
None
對列表調用append、sort、reverse這類在原處的修改的運算,一定是對列表做原處的修改,但這些方法在列表修改後並不會立即把列表返回。事實上,它們返回的是none對象。
爲什麼要注意print和stdout:
print語句和sys.stdout之間的等效是很重要的額,這樣纔有可能把sys.stdout重新賦值給用戶定義的對象(提供和文件相同的方法,就像write)。因爲print語句只是傳送文本給sys.stdout.write方法,可以把sys.stdout賦值給一個對象,而由該對象的write方法通過任意方式處理文字,通過這個對象捕捉程序中打印的文本。
第十二章:if測試
python語法規則:
第十三章:while和for循環
關於循環else分句是python特有的,簡而言之,循環else分句提供了常見的編寫代碼的明確語法;這是編寫代碼的結構,讓你捕捉循環的“另一條”出路,而不是通過設定和檢查標誌位或條件。
迭代器:
迭代工具包括了for循環、列表解析、in成員關係測試以及map內置函數等。
“可迭代對象”的概念在python中是相當新穎的。基本上,這就是序列觀念的通用化:如果對象是實際保存的序列,或者可以在迭代工具環境中一次產生一個結果的對象,就被看作是可迭代。總之,可迭代對象包括實際序列和按照需求而計算的虛擬序列。
文件迭代器:
瞭解迭代器含義的最簡單的方法之一就是看一看它是如何與內置類型一起工作的。每一種有左至右掃描對象的工具都會使用迭代協議。python提供了兩個內置函數,在for循環內定製迭代:
for循環一般都比while計數器循環運行得更快。
循環計數器:while和range
非完備遍歷:range
修改列表:range
並行遍歷:zip和map
>>> L1 = [1, 2, 3, 4]
>>> L2 = [5, 6, 7, 8]
>>> zip(L1, L2)
<zip object at 0x023583A0>
>>> for (x,y) in zip(L1,L2):
print(x, y, '-->', x+y)
1 5 --> 6
2 6 --> 8
3 7 --> 10
4 8 --> 12
>>> T1,T2, T3 = (1, 2, 3), (4, 5, 6), (7, 8, 9)
>>> T3
(7, 8, 9)
>>> zip(T1, T2, T3)
<zip object at 0x023585F8>
>>> #當參數長度不同時,zip會以最短序列的長度爲準來截斷所得到的元組
>>> S1 = 'abc'
>>> S2 = 'xyz123'
>>> zip(S1, S2)
<zip object at 0x023586E8>
使用zip構造字典:
>>> keys = ['spam', 'eggs', 'toast']
>>> vals = [1, 3, 5]
>>> D = dict(zip(keys, vals))
>>> D
{'toast': 5, 'eggs': 3, 'spam': 1}
產生偏移和元素:enumerate
>>> S = 'spam'
>>> for (offset, item) in enumerate(S):
print(item, 'appears at offset', offset)
s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3
enumerate函數返回一個生成器對象:這種對象支持迭代協議,有一個next方法,每次遍歷列表時,會返回一個(index,value)的元組,而我們能在for中通過元組賦值運算將其分解(很像是使用zip)
列表解析初探:
>>> L = [x + 10 for x in range(10)]
>>> L
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
列表解析和for循環語句並不完全相同,因其創建新的列表對象(如果原始列表有多個引用值,這一點可能就很重要)。
列表解析基礎:
從語法上講,列表解析的語法是從集合理論表示法中的一種結構中衍生出來的,也就是對集合中的每個元素應用某一種運算,但不需要懂得集合理論就能運用。在python中,多數人會覺得列表解析看起來就像是倒過來的for循環。列表解析是寫在方括號中的,因爲它畢竟是一種創建新的列表的方式。爲了執行表達式,python會在解釋器內通過L來執行迭代,依次把x賦值給每個元素,然後通過左側的表達式運行每一個元素並且存儲其結果。我們所得到的結果列表就是列表解析所說的內容:新列表表,內含x+10,而x是在L中的每個元素。
從技術角度來說,列表解析絕不是必須要求的,因爲我們可以使用for循環,在遍歷過程中把表達式結果加在列表上。
>>> res = []
>>> for x in L:
res.append(x+10)
>>> res
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
事實上,這正是列表解析內部所做的事。
擴展列表解析語法
第十四章:文檔
dir函數是抓取對象內可用屬性列表的簡單方式。塌能夠調用任何有屬性的對象。
>>> import sys
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_getframe', '_mercurial', '_xoptions', 'api_version', 'argv', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 'hash_info', 'hexversion', 'int_info', 'intern', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'setcheckinterval', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoptions', 'winver']
內置文檔字符串:
>>> print(sys.__doc__)
This module provides access to some objects used or maintained by the
interpreter and to functions that interact strongly with the interpreter.
Dynamic objects:
argv -- command line arguments; argv[0] is the script pathname if known
path -- module search path; path[0] is the script directory, else ''
modules -- dictionary of loaded modules
……
PyDoc:help函數
>>> help(sys.getrefcount)
Help on built-in function getrefcount in module sys:
getrefcount(...)
getrefcount(object) -> integer
Return the reference count of object. The count returned is generally
one higher than you might expect, because it includes the (temporary)
reference as an argument to getrefcount().
python的標準手冊:www.python.org上在線閱讀
常見編寫代碼的陷阱:
第四部分:函數
第十五章:函數基礎
函數是python爲了代碼最大程度的重用和最小化代碼冗餘而提供的最基本的程序結構。
概念介紹:
python中的多態:
python將對某一對象在某種語法的合理性交由對象自身來判斷。這種依賴類型的行爲成爲多態,其含義是一個操作的意義取決於被操作對象的類型。
第十六章:作用域和參數
變量名解析:LEGB原則
對於一個def語句:
全局變量和模塊的屬性石的等效的!
作用域和嵌套函數:
嵌套作用域的細節:對於一個函數:
例如:
工廠函數有時用於需要及時生成事件處理,實時對不同情況進行反饋的程序中(例如,用戶的輸入是無法進行預測的)。如:
>>> def maker(N):
def action(X):
return X ** N
return action
#這定義了一個外部的函數,這個函數簡單的生成並返回了一個嵌套的函數,卻並不調用這個內嵌的函數。如果我們調用外部的函數:
>>> f = maker(2)
>>> f
<function action at 0x0250A108>
我們得到的是生成的內嵌函數的一個引用。一個內嵌函數是通過運行內嵌的def而創建的,如果現在調用從外部得到的那個函數的話:
>>> f(3)
9
>>> f(4)
16
使用默認參數來保留嵌套作用域的狀態。
(P344)嵌套作用域和lambda:
>>> def func():
x = 4
action = (lambda n: x ** n)
return action
>>> x = func()
>>> print(x(2))
16
傳遞參數:
(P381)對參數輸出進行模擬
函數參數匹配:
語法 位置 解釋
func(value) 調用者 常規參數:通過位置進行匹配
func(name = value)調用者 關鍵字參數:通過變量名匹配
func(*name) 調用者 以name傳遞所有的對象,並作爲獨立的基於位置的參數
func(**name) 調用者 以name成對的傳遞所有的關鍵字/值,並作爲獨立的關鍵字參數
def func(name) 函數 常規參數:通過位置或變量名進行匹配
def func(name=value)函數 默認參數值,如果沒有在調用中傳遞的話
def func(*name) 函數 匹配並收集(在元組中)所有包含位置的參數
def func(**name) 函數 匹配並收集(在字典中)所有包含位置的參數
注意:在調用中,通過變量名進行匹配關鍵字,而在函數頭部,它爲一個可選的參數定義了默認值。無論是哪種情況,這都不是一個賦值語句。它是在這兩種情況下的特定語法,改變了默認的參數匹配機制。
分解參數:
在調用函數時能夠使用*語法。在這種情況下與函數定義的意思相反,它會分解參數的集合,而不是創建參數的集合。相似地,在函數調用時,**會以鍵值對的形式分解一個字典。
>>> def func(a, b, c, d):
print(a, b, c, d)
>>> args = {'a': 1, 'b': 2, 'c': 3}
>>> args['d'] = 4
>>> func(**args)
1 2 3 4
>>>
min調用:
>>> def min1(*args):
res = args[0]
for arg in args[1:]:
if arg < res:
res = arg
>>> def min1(*args):
res = args[0]
for arg in args[1:]:
if arg < res:
res = arg
return res
>>> def min2(first, *rest):
for arg in rest:
if arg < first:
first = arg
return first
>>> def min3(*args):
tmp = list(args)
tmp.sort()
return tmp[0]
>>> print(min1(3, 4, 1, 2))
1
>>> print(min2("bb", "aa"))
aa
>>> print(min3([2,2],[1,1],[3,3]))
[1, 1]
>>>
>>> def minmax(test, *args):
res = args[0]
for arg in args[1:]:
if test(arg, res):
res = arg
return res
>>> def lessthan(x, y):
return x<y
>>> def morethan(x, y):
return x>y
>>> print(minmax(lessthan, 4, 2, 1, 5, 6, 3))
1
>>> print(minmax(morethan, 4, 2, 1, 5, 6, 3))
6
第十七章:函數的高級話題
匿名函數:lambda
lambda就像def一樣,這個表達式創建了一個之後能夠調用的函數,但是它返回了一個函數而不是將這個函數賦值給一個變量名。這也就是lambda有時被稱作匿名的函數的原因。實際上,它常以一種行內進行函數定義的形式被使用,或者用作推遲執行一些代碼。
在程序對列表和其他序列常常要做的一件事就是對每一個元素進行一個操作並把其結果集合起來。例如:
>>> counters = [1, 2, 3, 4]
>>> update = []
>>> for x in counters:
update.append(x+10)
>>> update
[11, 12, 13, 14]
>>> def inc(x):
return x+10
>>> map(inc,counters)
<map object at 0x023D08D0>
函數式編程工具:filter和reduce
重訪列表解析:映射
列表解析和矩陣
重訪迭代器:生成器
不像一般的函數會生成值後退出,生成器函數在生成值後自動掛起並暫停它們的執行和狀態。
生成器和一般的函數之間代碼上的最大不同就是一個生成器yield一個值,而不是return一個值。yield語句將會將函數關起,並向它的調用者返回一個值,但是保存足夠的狀態信息爲了讓其能夠在函數從它掛起的地方恢復。這能夠允許這些函數不斷的產生一系列的值,而不是一次計算所有的值,之後將值以類似列表之類的形式來返回。
生成器函數在python中與迭代器協議的概念聯繫在一起,簡而言之,包含了yield語句的函數將會特地編譯爲生成器,當調用時,它們返回了一個生成器對象,這個生成器對象支持迭代器對象接口。生成器函數也許也有一個return語句,這個語句就是用來終止產生值的。
迭代器對象,依次定義了下一個方法,這些方法既要返回在迭代中的下一個元素,又要拋出一個特定的異常(StopIteration)來終結這個迭代,迭代器是通過內置的iter函數獲取的。如果協議支持的話,python的for循環使用了這個迭代接口協議來步進處理一個序列(或者序列生成器);如果不支持的話,for將會重複對序列進行索引運算。
擴展生成器函數協議:send和next
生成器表達式:迭代器遇到列表解析
在最新版本的python中,迭代器和列表解析的概念形成了這個語言的而一個新的特性,生成器表達式,從語法上來講,生成器表達式就像一般的列表解析一樣,但是它們是括在圓括號而不是方括號中的。
>>> [x ** 2 for x in range(4)]
[0, 1, 4, 9]
>>> (x ** 2 for x in range(4))
<generator object <genexpr> at 0x023D1FA8>
從執行過程上來講,生成器表達式很不相同:不是在內存中構建結果,而是返回一個生成器對象,這個對象將會支持迭代協議並在任意的迭代語境的操作中,獲得最終結果列表的一部分。
對迭代的各種方法進行計時:
列表解析要比for循環語句有速度方面的性能優勢,而且map會依據調用方法的不同表現出更好或更差的性能。生成器表達式看起來比列表解析速度更慢一些,但是它們把內存需求降到了最小。
函數設計概念:
如何將任務分解成更有針對性的函數(導致了聚合性)、函數將如何通信(耦合性)等。
函數陷阱:
本地變量是靜態檢測的
沒有return語句的函數
>>> list = [1, 2, 3]
>>> list = list.append(4)
>>> print(list)
None
>>> list.append(5)
Traceback (most recent call last):
File "<pyshell#88>", line 1, in <module>
list.append(5)
AttributeError: 'NoneType' object has no attribute 'append'
>>> list = [1, 2,3]
>>> list.append(4)
>>> print(list)
[1, 2, 3, 4]
嵌套作用域的循環變量
第五部分:模塊
模塊:宏偉藍圖
import :使客戶端(導入者)以一個整體獲取一個模塊。
from :允許客戶端從一個模塊文件中獲取特定的變量名。
reload : 在不中止python程序的情況下,提供了一種重新載入模塊文件代碼的方法。
模塊至少有三個角色:
(409/PDF440)import如何工作
第十九章:模塊代碼編寫基礎
模塊怎麼命名都可以,但是如果打算將其導入,模塊文件名就應該以.py結尾,對於會執行但不會被導入的頂層文件而言,.py從技術上來講是可有可無的,但是每次都加上去,可以確保文件類型更醒目,並允許以後可以導入任何文件。
導入只發生一次,因爲模塊文件中的頂層程序代碼通常只執行一次,你可以憑藉這種特性對變量進行初始化。
import和from是賦值語句:
就像def一樣,import和from是可執行的語句,而不是編譯期間的聲明,而且它們可以嵌套在if測試中,出現在函數def之中等等,直到執行程序時,python執行到這些語句,纔會進行解析,換句話說,被導入的模塊和變量名直到它們所對應的import或from語句執行後,纔可以使用,此外,就像def一樣,import和from都是隱性的賦值語句。
from只是把變量名從一個模塊複製到另一個模塊,並不會對模塊本身進行賦值。
第二十章 模塊包
import dir1.dir2.mod
必須遵循下列規則:
第二十一章 高級模塊話題
在模塊中隱藏數據:python的封裝更像是打包,而不是約束。
最小化from*的破壞:_X和__all__
__all__是指出要複製的變量名,而_X是指出不被複制的變量名。
培養他還能會先尋找模塊內的__all__列表,如果沒有定義的話,from*就會
複製出開頭沒有單下劃線的所有變量名。就像_X慣例一樣,__all__列表只對from*語句這種形式有效,它並不是私有聲明。
啓用以後的語言特性:
from __future__ import featurename
混合用法模式:__name__和__main__
以__name__進行單元測試
修改模塊搜索路徑:模塊搜索路徑是一個目錄列表,可以通過環境變量PYTHONPATH以及可能的.pth路徑文件進行定製。sys.path的設置方法只在修改的python會話或程序中才會存續。在python結束後,不會保留下來。
import as 擴展:
import longmodulename as name相當於:
import longmodulename
name = longmodulename
del longmodulename #don't keep original name
from module import longname as name
相對導入語法
模塊設計理念:
模塊式對象:元程序
reload的使用沒有傳遞性:當重載一個模塊時,python只會重載那個模塊的文件,不會自動重載該文件重載時碰巧還要導入的模塊。
遞歸形式的from import無法工作。
第六部分 類和OOP
第二十二章 OOP:宏偉藍圖
類是在python實現支持繼承的新種類的對象的部件。
屬性繼承搜索
類:
類是實例工廠。類的屬性提供了行爲(數據以及函數),所有從類產生的實例都繼承該類的屬性。
實例:
代表程序中具體的元素。實例屬性記錄數據,而每個特定對象的數據都不同。
類和實例的主要差別在於,類是一種產生實例的工廠。
類和模塊的另一個差異:內存中特定模塊只有一個實例(所以我們得重載模塊以取得其新代碼),但是,對於類而言,只要有需要,製作多少實例都可以。
在OOP中,實例就像是帶有“數據”的記錄,而類是處理這些記錄的“程序”。
編寫類樹
就像簡單變量一樣,類和實例屬性並沒有事先聲明,而是在首次賦值時它的值纔會存在,類樹中所有對象都不過是命名空間對象,我們可以通過恰當的變量名讀取或設置其任何屬性。
大體而言,OOP就是在樹中搜索屬性。
第二十三章 類代碼編寫基礎
類產生多個實例對象(P516)
在python中,實例從類中繼承,而類繼承於超類。
1、超類列在了類開頭的括號中。
2、類從其超類中繼承屬性。
3、實例會繼承所有可讀取類的屬性。
4、每個object.attribute都會開啓新的獨立搜索。
5、邏輯的修改是通過創建子類,而不是修改超類。
這種搜索的結果和主要目的就是,類支持了程序的分解和定製。
字典是python中最具靈活的內置數據結構類型。如果把列表看作是有序的對象集合,那麼就可以把字典當成是無序的集合。它們主要的差別在於:字典當中的元素是通過鍵來存取的,而不是通過偏移存取。
作爲內置類型,字典可以取代許多搜索算法和數據結構。
主要屬性如下:
通過鍵而不是偏移量來讀取;
任意對象的無序集合;
可變長、異構、任意嵌套;
屬於可變映射類型;
對象引用表(哈希表);
與列表不同的是,當對新字典鍵進行賦值(之前沒有被賦值的鍵),就會再字典內生成一個新的元素;在列表中情況不同,因爲python會將超出列表末尾的偏移視爲越界並報錯。要想擴展列表,你需要使用append方法或分片賦值來實現。
其他字典方法:
>>> d2 = {'spam': 2, 'ham': 1, 'eggs' : 3}
>>> d2.values()
dict_values([3, 1, 2])
>>> d2.items()
dict_items([('eggs', 3), ('ham', 1), ('spam', 2)])
>>> d2.get('spam')
2
字典的update方法有點類似於合併,它把一個字典的鍵和值合併到另一個,盲目地覆蓋相同鍵的值。
>>> d2
{'eggs': 3, 'ham': 1, 'spam': 2}
>>> d3 = {'toast': 4, 'mu':5}
>>> d2.update(d3)
>>> d2
{'toast': 4, 'mu': 5, 'eggs': 3, 'ham': 1, 'spam': 2}
>>>
字典pop方法能夠從字典中刪除一個鍵並返回它的值。這類似於列表的pop方法,只不過刪除的是一個鍵而不是一個可選的位置。
>>> d2
{'toast': 4, 'mu': 5, 'eggs': 3, 'ham': 1, 'spam': 2}
>>> d2.pop('mu')
5
>>> d2
{'toast': 4, 'eggs': 3, 'ham': 1, 'spam': 2}
>>>
>>> # pop a list by position
>>> L = ['aa', 'bb', 'cc', 'dd']
>>> L.pop()
'dd'
>>> L
['aa', 'bb', 'cc']
>>> L.pop(1)
'bb'
>>> L
['aa', 'cc']
>>>
語言表:
>>> table ={'python' : 'Guido van Rossum',
'perl': 'Larry Wall',
'Tcl': 'John Ousterhout'}
>>> language = 'python'
>>> creator = table[language]
>>> creator
'Guido van Rossum'
>>> for lang in table.keys():
print(lang, '\t', table[lang])
python Guido van Rossum
Tcl John Ousterhout
perl Larry Wall
>>>
字典用法注意事項:
- 序列運算無效。字典是映射機制,不是序列。
- 對新索引賦值會添加項。
- 鍵不一定總是字符串。
- 在if語句中預先對鍵進行測試
- 使用try語句明確地捕獲並修復這一異常
- get方法爲不存在的鍵提供一個默認值。
>>> {'name': 'mel', 'age': '45'}
{'age': '45', 'name': 'mel'}
>>> d ={}
>>> d['name'] = 'mel'
>>> d['age'] = '45'
>>> d
{'age': '45', 'name': 'mel'}
>>>
>>> dict(name = 'mel', age = '45')
{'age': '45', 'name': 'mel'}
>>>
>>> dict([('name', 'mel'), ('age', '45')])
{'age': '45', 'name': 'mel'}
>>>
這四種形式都會建立相同的兩鍵字典:
- 如果你可以事先拼出整個字典,那麼第一種是很方便的;
- 如果你需要一次動態地建立字典的一個字段,第二種比較合適;
- 第三種關鍵字形式所需的代碼比較常量少,但是鍵必須都是字符串;
- 如果你需要在程序運行時把鍵和值逐步建成序列,那麼最後一種形式比較有用。
>>> dict.fromkeys(['a', 'b'], 0)
{'a': 0, 'b': 0}
>>>
第九章 元組(tuple)、文件及其他
元組:
python集合類型是元組(tuple)。元組由簡單的對象組構成,元組與列表非常類似,只不過元組不能在原處修改(它們是不可變的),並且通常寫成圓括號(而不是方括號)中的一系列項。元組不支持任何方法調用,但元組具有列表的大多數屬性。
任意對象的有序集合:與字符串和列表類似,元組是一個位置有序的對象的集合。
通過偏移存取:同字符串、列表一樣,支持所有基於偏移的操作:索引和分片。
屬於不可變序列類型:類似於字符串,不支持應用在任何原處修改操作。
固定長度、異構、任意嵌套:因爲元組是不可變的,在不生成一個拷貝的情況下不能增長或縮短。另一方面,元組可以包含其他的複合對象(例如:列表、字典、其他元組等),因此支持嵌套。
對象引用的數組:與列表相似,元組最好被認爲是對象引用的數組。
元組的特殊語法:逗號和圓括號
如果圓括號裏的單一對象是元組對象而不是一個簡單的表達式時,需要對python進行特別說明。如果確實想得到一個元組,只要在這個單個元素之後、關閉圓括號之前加一個逗號就可以了。
>>> x = (40)
>>> x
40
>>> x = (40,)
>>> x
(40,)
>>>
作爲特殊情況,在不引起語法衝突的情況下,python允許忽略元組的圓括號。僅當元組作爲文字傳給函數調用(圓括號很重要)以及當元組在print語句中列出(逗號很重要)的特殊情況時,圓括號纔是必不可少的。
轉換以及不可變性:
如果想對元組進行排序,通常先得將它轉換爲列表才能獲得使用排序方法調用的權限,並將它變爲一個可變對象。
>>> T = ('cc', 'aa', 'dd', 'bb')
>>> tmp = list(T)
>>> tmp.sort()
>>> tmp
['aa', 'bb', 'cc', 'dd']
>>> T = tuple(tmp)
>>> T
('aa', 'bb', 'cc', 'dd')
>>>
列表解析(list comprehension)也可以用於元組的轉換。
>>> T = (1, 2, 3, 4, 5)
>>> L = [x + 20 for x in T]
>>> L
[21, 22, 23, 24, 25]
>>>
列表解析是名副其實的序列操作——它們總會創建新的列表,但也可以用於遍歷包括元組、字符串以及其他列表在內的任何序列對象。列表解析甚至可以用在某些並非實際存儲的序列之上——任何可遍歷的對象都可以,包括可自動逐行讀取的文件。
注意元組的不可變性只適用於元組本身頂層而並非其內容。例如,元組內部的列表是可以像往常那樣修改的。
>>> T = (1, [2, 3, 4], 5)
>>> T[1] = 'spam'
Traceback (most recent call last):
File "<pyshell#61>", line 1, in <module>
T[1] = 'spam'
TypeError: 'tuple' object does not support item assignment
>>> T[1][1] = 'spam'
>>> T
(1, [2, 'spam', 4], 5)
>>>
爲什麼有了列表還要元組:
元組的不可變性提供了某種完整性。元組也可以用在列表無法使用的地方。例如,作爲字典鍵。
文件:
內置open函數會創建一個python文件對象,可以作爲計算機上的一個文件鏈接。文件對象不是數字、序列也不是映射。
記住:現在從文本文件讀取文字行的最佳方式是根本不要讀取該文件。文件也有個迭代器會自動地在for循環、列表解析或者其他迭代語句中對文件進行逐行讀取。
實際應用中的文件:
>>> myfile = open('myfile', 'w')
>>> myfile.write('hello text file\n')
16
>>> myfile.close()
>>> myfile = open('myfile')
>>> myfile.readline()
'hello text file\n'
>>> myfile.readline()
''
寫入方法不會爲我們添加行終止符,所以程序必須包含它來嚴格地終止行。
在文件中存儲並解析python對象:
>>> F = open('datafile.txt', 'w')
>>> F.write(S + '\n')
5
>>> F.write('%s,%s,%s\n' % (X, Y, Z))
9
>>> F.write(str(L) + '$' + str(D) + '\n')
27
>>> F.close()
>>> bytes = open('datafile.txt').read()
>>> bytes
"spam\n43,44,45\n[1, 2, 3]${'a': 1, 'b': 2}\n"
>>> print(bytes)
spam
43,44,45
[1, 2, 3]${'a': 1, 'b': 2}
>>> F = open('datafile.txt')
>>> line = F.readline()
>>> line
'spam\n'
>>> line.rstrip()
'spam'
>>>
>>> line = F.readline()
>>> line
'43,44,45\n'
>>> parts = line.split(',')
>>> parts
['43', '44', '45\n']
>>>
#int 能夠把數字字符串轉換爲整數對象,注意:我們不一定非要運行rstrip來刪除最後部分的“\n”,int 和一些其他轉換方法會忽略數字旁邊的空白。
['43', '44', '45\n']
>>> int(parts[1])
44
>>> number = [int(p) for p in parts]
>>> number
[43, 44, 45]
#轉換列表和字典
#使用eval可以把字符串轉換成對象
>>> parts = line.split('$')
>>> parts
['[1, 2, 3]', "{'a': 1, 'b': 2}\n"]
>>> eval(parts[0])
[1, 2, 3]
>>> objects = [eval(p) for p in parts]
>>> objects
[[1, 2, 3], {'a': 1, 'b': 2}]
>>>
用pickle存儲python的原生對象:
pickle模塊是能夠讓我們直接在文件中存儲幾乎任何python對象的高級工具,也並不要求我們把字符串轉換來轉換去。它就像是超級通用的數據格式化和解析工具。例如,想要在文件中存儲字典,就直接用pickle來存儲。
對象靈活性:
- 列表、字典和元組可以包含任何種類的對象
- 列表、字典和元組可以任意嵌套
- 列表和字典可以動態地擴大和縮小
賦值操作總是存儲對象的引用,而不是這些對象的拷貝。
拷貝:
- 沒有限制條件的分片表達式(L[ : ])能夠複製序列
- 字典copy方法(D.copy())能夠複製字典
- 有些內置函數(例如,list)能夠生成拷貝(list(L))
- copy標準庫模塊能夠生成完整拷貝
比較、相等性和真值
>>> L1 = [1, ('a', 3)]
>>> L2 = [1, ('a', 3)]
>>> L1 == L2, L1 is L2
(True, False)
L1,L2被賦值爲列表,雖然相等,卻是不同的對象。
- “==”操作符測試值的相等性。python運行相等測試,遞歸地比較所有內嵌對象
- “is”表達式測試對象的一致性。python測試二者是否是同一對象。
>>> S2 = 'spam'
>>> S1 ==S2, S1 is S2
(True, True)
在這裏,兩個截然不同的對象碰巧有着相同的值:但是因爲在python內部暫時存儲並重複使用短字符串作爲最佳化,事實上內存裏只有一個字符串‘spam’供S1和S2分享。因此,“is”一致性測試結果爲真,爲了得到更一般的結果,我們需要使用更長的字符串:
>>> S1 = 'a longer string'
>>> S2 = 'a longer string'
>>> S1 == S2, S1 is S2
(True, False)
相對大小的比較也能夠遞歸地應用於嵌套的數據結構。
>>> L1 = [1, ('a', 3)]
>>> L2 = [1, ('a', 2)]
>>> L1 < L2, L1 == L2, L1> L2
(False, False, True)
一般來說,python中不同的類型的比較方法如下:
- 數字通過相對大小進行比較
- 字符串是按照字典順序,一個字符接一個字符地對比進行比較
- 列表和元組從左到右對每部分的內容進行比較
- 字典通過排序之後的(鍵、值)列表進行比較
>>> L = [] * 100
>>> L
[]
>>> L = [None] * 100
>>> L
[None, None, None, ……]
對於列表來說,只能給已經存在的偏移賦值的。預先分配一個100項的列表,這樣你可以在100個偏移的任何一個加上None對象。
重複能夠增加層次深度:
序列重複就好像是多次將一個序列加到自己身上。
>>> L = [4,5,6]
>>> X = L * 4
>>> Y = [L] * 4
>>> X
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> Y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
>>> L[1] = 0
>>> X
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> Y
[[4, 0, 6], [4, 0, 6], [4, 0, 6], [4, 0, 6]]
重複、合併以及分片只是在複製操作數對象的頂層。
留意循環數據結構
>>> L = ['grail']
>>> L.append(L)
>>> L
['grail', [...]]
第三部分:語句和語法
第十章:python語句簡介:
python程序結構:
- 程序由模塊構成
- 模塊包含語句
- 語句包含表達式
- 表達式建立並處理對象
第十一章:賦值、表達式和打印:
賦值語句:
- 賦值語句建立對象引用值。python變量更像是指針,而不是數據存儲區域
- 變量名在首次賦值時會被創建。一旦賦值了,每當這個變量名出現在表達式時,就會被其所引用值取代
- 變量名在引用前必須先賦值。
- 隱式賦值語句:import、from、def、class、for、函數參數。賦值語句會在許多情況下使用。例如:模塊導入、函數和類的定義、for循環變量以及函數參數全都是隱式賦值運算。
>>> L = [1, 2]
>>> M = L # L和M引用相同的對象
>>> L = L + [3, 4] # 合併生成一個新的對象,運行起來更慢一些
>>> L, M
([1, 2, 3, 4], [1, 2])
>>> L = [1, 2]
>>> M = L
>>> L += [3, 4] # 但是增強賦值+=真正意味着擴展
>>> L,M # M原地修改
([1, 2, 3, 4], [1, 2, 3, 4])
變量命名規則:
1、語法:(下劃線或字母)+ (任意數目的字母、數字或下劃線)
變量名必須以下劃線或字母開頭,而後面接任意數目的字母、數字或下劃線。
2、區分大小寫。
3、禁止使用保留字。python保留字都是小寫的,無法對保留字做賦值運算。
命名慣例:
- 以單一下劃線開頭的變量名(_x)不會被from module import *語句導入的
- 前後有下劃線的變量名(_ _x_ _)是系統定義的變量名,對解釋器有特殊意義
- 以兩下劃線開頭、但結尾沒有兩個下劃線的變量名(_ _x)是類的本地變量
- 通過交互模式運行時,只有單個下劃線的變量名(_)會保存最後表達式的結果
變量名沒有類型,但對象有。
表達式語句和在原處的修改
>>> L = [1, 2]
>>> L.append(3)
>>> L
[1, 2, 3]
>>> L = [1, 2]
>>> L = L.append(3)
>>> L
>>>
>>> print(L)
None
對列表調用append、sort、reverse這類在原處的修改的運算,一定是對列表做原處的修改,但這些方法在列表修改後並不會立即把列表返回。事實上,它們返回的是none對象。
爲什麼要注意print和stdout:
print語句和sys.stdout之間的等效是很重要的額,這樣纔有可能把sys.stdout重新賦值給用戶定義的對象(提供和文件相同的方法,就像write)。因爲print語句只是傳送文本給sys.stdout.write方法,可以把sys.stdout賦值給一個對象,而由該對象的write方法通過任意方式處理文字,通過這個對象捕捉程序中打印的文本。
第十二章:if測試
python語法規則:
- 語句是逐個運行得,除非你不這樣編寫。
- 塊和語句的邊界會自動被檢測。
- 複合語句=首行+“:”+縮進語句。
- 空白行、空格以及註釋通常都會被忽略。
- 文檔字符串(docstring)會被忽略,但會被保存並由工具顯示。
- python沒有變量類型聲明。
- 如果使用語法括號對,語句就可橫跨數行。
- 如果語句以反斜槓結尾,就可橫跨數行。(反斜槓幾乎都不再使用了)
- 三重引號字符串常量可以橫跨數行。
第十三章:while和for循環
關於循環else分句是python特有的,簡而言之,循環else分句提供了常見的編寫代碼的明確語法;這是編寫代碼的結構,讓你捕捉循環的“另一條”出路,而不是通過設定和檢查標誌位或條件。
迭代器:
迭代工具包括了for循環、列表解析、in成員關係測試以及map內置函數等。
“可迭代對象”的概念在python中是相當新穎的。基本上,這就是序列觀念的通用化:如果對象是實際保存的序列,或者可以在迭代工具環境中一次產生一個結果的對象,就被看作是可迭代。總之,可迭代對象包括實際序列和按照需求而計算的虛擬序列。
文件迭代器:
瞭解迭代器含義的最簡單的方法之一就是看一看它是如何與內置類型一起工作的。每一種有左至右掃描對象的工具都會使用迭代協議。python提供了兩個內置函數,在for循環內定製迭代:
- 內置range函數返回連續整數列表,可作爲for中的索引。
- 內置zip函數返回並行的元素元組的列表,可用於在for中內遍歷數個序列。
for循環一般都比while計數器循環運行得更快。
循環計數器:while和range
非完備遍歷:range
修改列表:range
並行遍歷:zip和map
>>> L1 = [1, 2, 3, 4]
>>> L2 = [5, 6, 7, 8]
>>> zip(L1, L2)
<zip object at 0x023583A0>
>>> for (x,y) in zip(L1,L2):
print(x, y, '-->', x+y)
1 5 --> 6
2 6 --> 8
3 7 --> 10
4 8 --> 12
>>> T1,T2, T3 = (1, 2, 3), (4, 5, 6), (7, 8, 9)
>>> T3
(7, 8, 9)
>>> zip(T1, T2, T3)
<zip object at 0x023585F8>
>>> #當參數長度不同時,zip會以最短序列的長度爲準來截斷所得到的元組
>>> S1 = 'abc'
>>> S2 = 'xyz123'
>>> zip(S1, S2)
<zip object at 0x023586E8>
使用zip構造字典:
>>> keys = ['spam', 'eggs', 'toast']
>>> vals = [1, 3, 5]
>>> D = dict(zip(keys, vals))
>>> D
{'toast': 5, 'eggs': 3, 'spam': 1}
產生偏移和元素:enumerate
>>> S = 'spam'
>>> for (offset, item) in enumerate(S):
print(item, 'appears at offset', offset)
s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3
enumerate函數返回一個生成器對象:這種對象支持迭代協議,有一個next方法,每次遍歷列表時,會返回一個(index,value)的元組,而我們能在for中通過元組賦值運算將其分解(很像是使用zip)
列表解析初探:
>>> L = [x + 10 for x in range(10)]
>>> L
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
列表解析和for循環語句並不完全相同,因其創建新的列表對象(如果原始列表有多個引用值,這一點可能就很重要)。
列表解析基礎:
從語法上講,列表解析的語法是從集合理論表示法中的一種結構中衍生出來的,也就是對集合中的每個元素應用某一種運算,但不需要懂得集合理論就能運用。在python中,多數人會覺得列表解析看起來就像是倒過來的for循環。列表解析是寫在方括號中的,因爲它畢竟是一種創建新的列表的方式。爲了執行表達式,python會在解釋器內通過L來執行迭代,依次把x賦值給每個元素,然後通過左側的表達式運行每一個元素並且存儲其結果。我們所得到的結果列表就是列表解析所說的內容:新列表表,內含x+10,而x是在L中的每個元素。
從技術角度來說,列表解析絕不是必須要求的,因爲我們可以使用for循環,在遍歷過程中把表達式結果加在列表上。
>>> res = []
>>> for x in L:
res.append(x+10)
>>> res
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
事實上,這正是列表解析內部所做的事。
擴展列表解析語法
第十四章:文檔
dir函數是抓取對象內可用屬性列表的簡單方式。塌能夠調用任何有屬性的對象。
>>> import sys
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_getframe', '_mercurial', '_xoptions', 'api_version', 'argv', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 'hash_info', 'hexversion', 'int_info', 'intern', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'setcheckinterval', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoptions', 'winver']
內置文檔字符串:
>>> print(sys.__doc__)
This module provides access to some objects used or maintained by the
interpreter and to functions that interact strongly with the interpreter.
Dynamic objects:
argv -- command line arguments; argv[0] is the script pathname if known
path -- module search path; path[0] is the script directory, else ''
modules -- dictionary of loaded modules
……
PyDoc:help函數
>>> help(sys.getrefcount)
Help on built-in function getrefcount in module sys:
getrefcount(...)
getrefcount(object) -> integer
Return the reference count of object. The count returned is generally
one higher than you might expect, because it includes the (temporary)
reference as an argument to getrefcount().
python的標準手冊:www.python.org上在線閱讀
常見編寫代碼的陷阱:
- 別忘了冒號;
- 從第1行開始;
- 空白行在交互模式提示符下很重要;
- 縮進要一致;
- 不要在python中寫C代碼:(例如:if (x == 1): )不需要(),不要在while循環測試中嵌入賦值語句;
- 使用簡單的for循環,而不是while或range;
- 要注意賦值語句中的可變對象:(例如:a = b =[],以及在增強指定語句中a += [1, 2],在原處的修改會影響其他變量。)
- 不要期待進行在原處的修改的函數會返回結果(像list.append和List.sort方法這種的修改,並不會有返回值(除了None)。所以調用時不要對其賦值。)
- 一定要使用括號調用函數。(必須在函數名稱後面加括號才能對它進行調用,無論它是否帶參數。函數也是對象,通過括號觸發對它的調用)
- 不要在導入和重載中使用擴展名或路徑(在import語句中省略目錄路徑和文件字尾,如:import mod,而不是import mod.py)
第四部分:函數
第十五章:函數基礎
函數是python爲了代碼最大程度的重用和最小化代碼冗餘而提供的最基本的程序結構。
概念介紹:
- def是可執行的代碼。def是一個可執行的語句——函數並不存在,直到python運行了def後才存在。在典型的操作中,def語句在模塊文件中編寫,並自然而然地在模塊文件第一次被導入的時候生成定義的函數。
- def創建了一個對象並將其賦值給某一變量名。當python運行到def語句時,它將會生成一個新的函數對象並將其賦值給這個函數名。就像所有的賦值一樣,函數名變成了某一函數的引用。函數也可以通過lambda表達式來創建。
- return將一個結果對象發送給調用者。
- 函數是通過賦值(對象引用)傳遞的。在python中,參數通過賦值傳遞給函數。
- global聲明瞭一個模塊級的變量並被賦值。在默認情況下,所有在一個函數中被賦值的對象,是這個函數的本地變量,並且僅在這個函數運行得過程中存在。變量名需要關注它的作用域(也就是變量存儲的地方),並且是通過實賦值語句將變量名綁定至作用域的。
- 參數、返回值以及變量並不是聲明。在函數中一樣沒有類型約束。可以傳遞任意類型的參數給函數,函數也可以返回任意類型的對象。
python中的多態:
python將對某一對象在某種語法的合理性交由對象自身來判斷。這種依賴類型的行爲成爲多態,其含義是一個操作的意義取決於被操作對象的類型。
第十六章:作用域和參數
變量名解析:LEGB原則
對於一個def語句:
- 變量名引用分爲三個作用域進行查找:首先是本地,之後是函數內(如果有的話),之後是全局,最後是內置。
- 在默認情況下,變量名賦值會創建或者改變本地變量。
- 全局聲明將賦值變量名映射到模塊文件內部的作用域。
- 當在函數中使用未認證的變量名時,python搜索4個作用域【本地作用域(L),之後是上一層結構中def或lambda的本地作用域(E),之後是全局作用域(G),最後是內置作用域(B)】並且在第一處能夠找到這個變量名的地方停下來。變量名在使用前首先必須被賦值過。
- 當在函數中給一個變量名賦值時(而不是在一個表達式中對其進行引用),python總是創建或改變本地作用域的變量名,除非它已經在那個函數中聲明爲全局變量。
- 當在函數之外給一個變量名賦值時(也就是,在一個模塊文件的頂層,或者是在交互提示模式下),本地作用域與全局作用域(這個模塊的命名空間)是相同的。
全局變量和模塊的屬性石的等效的!
作用域和嵌套函數:
嵌套作用域的細節:對於一個函數:
- 在默認情況下,一個賦值(X = value)創建或者改變了變量名X的當前作用域。如果X在函數內部聲明爲全局變量,它將會創建或改變變量名X爲整個模塊的作用域。
- 一個引用(X)首先在本地(函數內)作用域查找變量名X,之後會在代碼的語法上嵌套了的函數中的本地作用域,從內到外,之後查找當前的全局作用域(模塊文件),最後再內置作用域內(模塊__builtin__)。全局聲明將會直接從全局(模塊文件)作用域進行搜索。
例如:
工廠函數有時用於需要及時生成事件處理,實時對不同情況進行反饋的程序中(例如,用戶的輸入是無法進行預測的)。如:
>>> def maker(N):
def action(X):
return X ** N
return action
#這定義了一個外部的函數,這個函數簡單的生成並返回了一個嵌套的函數,卻並不調用這個內嵌的函數。如果我們調用外部的函數:
>>> f = maker(2)
>>> f
<function action at 0x0250A108>
我們得到的是生成的內嵌函數的一個引用。一個內嵌函數是通過運行內嵌的def而創建的,如果現在調用從外部得到的那個函數的話:
>>> f(3)
9
>>> f(4)
16
使用默認參數來保留嵌套作用域的狀態。
(P344)嵌套作用域和lambda:
>>> def func():
x = 4
action = (lambda n: x ** n)
return action
>>> x = func()
>>> print(x(2))
16
傳遞參數:
- 參數的傳遞是通過自動將對象賦值給本地變量來實現的。作爲參數被傳遞的對象從來不自動被拷貝。
- 在函數內部的參數名的賦值不會影響調用者。在函數運行時,在函數頭部的參數名是一個新的、本地的變量名,這個變量名是在函數的本地作用域內的。函數參數名和調用者的變量名是沒有別名的。
- 改變函數的可變對象參數的值也許會對調用者有影響。因爲參數是簡單的通過賦值進行對象的傳遞的,函數能夠改變傳入的可變對象,因此其結果會影響調用者。可變參數對於函數來說是可以做輸入和輸出的。
- 不可變參數是“通過值”進行傳遞。
- 可變對象是通過“指針”進行傳遞的。
(P381)對參數輸出進行模擬
函數參數匹配:
語法 位置 解釋
func(value) 調用者 常規參數:通過位置進行匹配
func(name = value)調用者 關鍵字參數:通過變量名匹配
func(*name) 調用者 以name傳遞所有的對象,並作爲獨立的基於位置的參數
func(**name) 調用者 以name成對的傳遞所有的關鍵字/值,並作爲獨立的關鍵字參數
def func(name) 函數 常規參數:通過位置或變量名進行匹配
def func(name=value)函數 默認參數值,如果沒有在調用中傳遞的話
def func(*name) 函數 匹配並收集(在元組中)所有包含位置的參數
def func(**name) 函數 匹配並收集(在字典中)所有包含位置的參數
注意:在調用中,通過變量名進行匹配關鍵字,而在函數頭部,它爲一個可選的參數定義了默認值。無論是哪種情況,這都不是一個賦值語句。它是在這兩種情況下的特定語法,改變了默認的參數匹配機制。
分解參數:
在調用函數時能夠使用*語法。在這種情況下與函數定義的意思相反,它會分解參數的集合,而不是創建參數的集合。相似地,在函數調用時,**會以鍵值對的形式分解一個字典。
>>> def func(a, b, c, d):
print(a, b, c, d)
>>> args = {'a': 1, 'b': 2, 'c': 3}
>>> args['d'] = 4
>>> func(**args)
1 2 3 4
>>>
min調用:
>>> def min1(*args):
res = args[0]
for arg in args[1:]:
if arg < res:
res = arg
>>> def min1(*args):
res = args[0]
for arg in args[1:]:
if arg < res:
res = arg
return res
>>> def min2(first, *rest):
for arg in rest:
if arg < first:
first = arg
return first
>>> def min3(*args):
tmp = list(args)
tmp.sort()
return tmp[0]
>>> print(min1(3, 4, 1, 2))
1
>>> print(min2("bb", "aa"))
aa
>>> print(min3([2,2],[1,1],[3,3]))
[1, 1]
>>>
>>> def minmax(test, *args):
res = args[0]
for arg in args[1:]:
if test(arg, res):
res = arg
return res
>>> def lessthan(x, y):
return x<y
>>> def morethan(x, y):
return x>y
>>> print(minmax(lessthan, 4, 2, 1, 5, 6, 3))
1
>>> print(minmax(morethan, 4, 2, 1, 5, 6, 3))
6
第十七章:函數的高級話題
匿名函數:lambda
lambda就像def一樣,這個表達式創建了一個之後能夠調用的函數,但是它返回了一個函數而不是將這個函數賦值給一個變量名。這也就是lambda有時被稱作匿名的函數的原因。實際上,它常以一種行內進行函數定義的形式被使用,或者用作推遲執行一些代碼。
- lambda是一個表達式,而不是一個語句。lambda能夠出現在python語法不允許def出現的地方——例如,在一個列表常量中或函數調用中。此外,作爲一個表達式,lambda反悔了一個值(一個新的函數),可以選擇性地賦值給一個變量名。相反,def語句總是得在頭部將一個新的函數賦值給一個變量名,而不是將這個函數作爲結果返回。
- lambda的主體是一個單個的表達式,而不是一個代碼塊。lambda限制了程序的嵌套:lambda是一個爲編寫簡單的函數而設計的,而def用來處理更大的任務。
在程序對列表和其他序列常常要做的一件事就是對每一個元素進行一個操作並把其結果集合起來。例如:
>>> counters = [1, 2, 3, 4]
>>> update = []
>>> for x in counters:
update.append(x+10)
>>> update
[11, 12, 13, 14]
>>> def inc(x):
return x+10
>>> map(inc,counters)
<map object at 0x023D08D0>
函數式編程工具:filter和reduce
重訪列表解析:映射
列表解析和矩陣
重訪迭代器:生成器
不像一般的函數會生成值後退出,生成器函數在生成值後自動掛起並暫停它們的執行和狀態。
生成器和一般的函數之間代碼上的最大不同就是一個生成器yield一個值,而不是return一個值。yield語句將會將函數關起,並向它的調用者返回一個值,但是保存足夠的狀態信息爲了讓其能夠在函數從它掛起的地方恢復。這能夠允許這些函數不斷的產生一系列的值,而不是一次計算所有的值,之後將值以類似列表之類的形式來返回。
生成器函數在python中與迭代器協議的概念聯繫在一起,簡而言之,包含了yield語句的函數將會特地編譯爲生成器,當調用時,它們返回了一個生成器對象,這個生成器對象支持迭代器對象接口。生成器函數也許也有一個return語句,這個語句就是用來終止產生值的。
迭代器對象,依次定義了下一個方法,這些方法既要返回在迭代中的下一個元素,又要拋出一個特定的異常(StopIteration)來終結這個迭代,迭代器是通過內置的iter函數獲取的。如果協議支持的話,python的for循環使用了這個迭代接口協議來步進處理一個序列(或者序列生成器);如果不支持的話,for將會重複對序列進行索引運算。
擴展生成器函數協議:send和next
生成器表達式:迭代器遇到列表解析
在最新版本的python中,迭代器和列表解析的概念形成了這個語言的而一個新的特性,生成器表達式,從語法上來講,生成器表達式就像一般的列表解析一樣,但是它們是括在圓括號而不是方括號中的。
>>> [x ** 2 for x in range(4)]
[0, 1, 4, 9]
>>> (x ** 2 for x in range(4))
<generator object <genexpr> at 0x023D1FA8>
從執行過程上來講,生成器表達式很不相同:不是在內存中構建結果,而是返回一個生成器對象,這個對象將會支持迭代協議並在任意的迭代語境的操作中,獲得最終結果列表的一部分。
對迭代的各種方法進行計時:
列表解析要比for循環語句有速度方面的性能優勢,而且map會依據調用方法的不同表現出更好或更差的性能。生成器表達式看起來比列表解析速度更慢一些,但是它們把內存需求降到了最小。
函數設計概念:
如何將任務分解成更有針對性的函數(導致了聚合性)、函數將如何通信(耦合性)等。
- 耦合性:對於輸入使用參數並且對於輸出使用return語句。一般來講,你需要力求讓函數獨立於它外部的東西。參數和return語句通常就是隔離對外部的依賴關係的最好的辦法,從而讓代碼中只剩少量醒目位置。
- 耦合性:只有在真正必要的情況下使用全局變量。全局變量(在整個模塊中的變量名)通常是一種蹩腳的函數間進行通信的辦法。它們引發了依賴關係和計時的問題,會導致程序調試和修改的困難。
- 耦合性:不要改變可變類型的參數,除非調用者希望這樣做。函數會改變傳入的可變類型對象,但是就像全局變量一樣,這會導致很對調用者和被調用者之間的耦合性,這種耦合性會導致一耳光函數過於特殊和不友好。
- 聚合性:每一個函數都應該有一個單一的、統一的目標。在設計完美的情況下,每一個函數中都應該做一件事:這件事可以用一個簡單說明句來總結。
- 大小:每一個函數應該相對較小。保持簡單,保持簡短。
- 耦合:避免直接改變在另一個模塊文件中的變量。在可能的時候使用讀取函數,而不是直接進行賦值語句。
函數陷阱:
本地變量是靜態檢測的
沒有return語句的函數
>>> list = [1, 2, 3]
>>> list = list.append(4)
>>> print(list)
None
>>> list.append(5)
Traceback (most recent call last):
File "<pyshell#88>", line 1, in <module>
list.append(5)
AttributeError: 'NoneType' object has no attribute 'append'
>>> list = [1, 2,3]
>>> list.append(4)
>>> print(list)
[1, 2, 3, 4]
嵌套作用域的循環變量
第五部分:模塊
模塊:宏偉藍圖
import :使客戶端(導入者)以一個整體獲取一個模塊。
from :允許客戶端從一個模塊文件中獲取特定的變量名。
reload : 在不中止python程序的情況下,提供了一種重新載入模塊文件代碼的方法。
模塊至少有三個角色:
- 代碼重用:模塊可以在文件中永久保存代碼。除此之外,模塊還是定義變量名的空間,被認作是屬性,可以被多個外部的客戶端引用。
- 系統命名空間的劃分:模塊還是在python中最高級別的程序組織單元。從根本上來講,它們不過是變量名的軟件包。
- 實現共享服務和數據:從操作的角度來看,模塊對實現跨系統共享的組件是很方便的,而且只需要一個拷貝即可。
(409/PDF440)import如何工作
第十九章:模塊代碼編寫基礎
模塊怎麼命名都可以,但是如果打算將其導入,模塊文件名就應該以.py結尾,對於會執行但不會被導入的頂層文件而言,.py從技術上來講是可有可無的,但是每次都加上去,可以確保文件類型更醒目,並允許以後可以導入任何文件。
導入只發生一次,因爲模塊文件中的頂層程序代碼通常只執行一次,你可以憑藉這種特性對變量進行初始化。
import和from是賦值語句:
就像def一樣,import和from是可執行的語句,而不是編譯期間的聲明,而且它們可以嵌套在if測試中,出現在函數def之中等等,直到執行程序時,python執行到這些語句,纔會進行解析,換句話說,被導入的模塊和變量名直到它們所對應的import或from語句執行後,纔可以使用,此外,就像def一樣,import和from都是隱性的賦值語句。
- import將整個模塊對象賦值給一個變量名。
- from將一個或多個變量名賦值給另一個模塊中同名的對象。
from只是把變量名從一個模塊複製到另一個模塊,並不會對模塊本身進行賦值。
第二十章 模塊包
import dir1.dir2.mod
必須遵循下列規則:
- dir1和dir2中必須都含有一個__init__.py文件。
- dir0是容器,不需要__init__.py文件;如果有的話,這個文件也會被忽略。
- dir0(而非dir0\dir1)必須列在模塊搜索路徑上(也就是此目錄必須是主目錄,或者列在PYTHONPATH之中)。
第二十一章 高級模塊話題
在模塊中隱藏數據:python的封裝更像是打包,而不是約束。
最小化from*的破壞:_X和__all__
__all__是指出要複製的變量名,而_X是指出不被複制的變量名。
培養他還能會先尋找模塊內的__all__列表,如果沒有定義的話,from*就會
複製出開頭沒有單下劃線的所有變量名。就像_X慣例一樣,__all__列表只對from*語句這種形式有效,它並不是私有聲明。
啓用以後的語言特性:
from __future__ import featurename
混合用法模式:__name__和__main__
以__name__進行單元測試
修改模塊搜索路徑:模塊搜索路徑是一個目錄列表,可以通過環境變量PYTHONPATH以及可能的.pth路徑文件進行定製。sys.path的設置方法只在修改的python會話或程序中才會存續。在python結束後,不會保留下來。
import as 擴展:
import longmodulename as name相當於:
import longmodulename
name = longmodulename
del longmodulename #don't keep original name
from module import longname as name
相對導入語法
模塊設計理念:
- 總是在python的模塊內編寫代碼。
- 模塊耦合要降到最低:全局變量。
- 最大化模塊的粘合性:統一目標。
- 模塊應該少去修改其他模塊的變量。
模塊式對象:元程序
reload的使用沒有傳遞性:當重載一個模塊時,python只會重載那個模塊的文件,不會自動重載該文件重載時碰巧還要導入的模塊。
遞歸形式的from import無法工作。
第六部分 類和OOP
第二十二章 OOP:宏偉藍圖
類是在python實現支持繼承的新種類的對象的部件。
屬性繼承搜索
類:
類是實例工廠。類的屬性提供了行爲(數據以及函數),所有從類產生的實例都繼承該類的屬性。
實例:
代表程序中具體的元素。實例屬性記錄數據,而每個特定對象的數據都不同。
類和實例的主要差別在於,類是一種產生實例的工廠。
類和模塊的另一個差異:內存中特定模塊只有一個實例(所以我們得重載模塊以取得其新代碼),但是,對於類而言,只要有需要,製作多少實例都可以。
在OOP中,實例就像是帶有“數據”的記錄,而類是處理這些記錄的“程序”。
編寫類樹
- 每個class語句會生成一個新的類對象。
- 每次類調用時,就會生成一個新的實例對象。
- 實例自動連結至創建了這些實例的類。
- 類連結至超類的方式是,將超類列在類頭部的括號裏。其從左到右的順序會決定樹中的次序。
- 屬性通常是在class語句中通過賦值語句添加在類中,而不是嵌入在函數的def語句內。
- 屬性通常是在類內,對傳給函數的特殊參數(也就是self),做賦值運算而添加在實例中的。
就像簡單變量一樣,類和實例屬性並沒有事先聲明,而是在首次賦值時它的值纔會存在,類樹中所有對象都不過是命名空間對象,我們可以通過恰當的變量名讀取或設置其任何屬性。
大體而言,OOP就是在樹中搜索屬性。
第二十三章 類代碼編寫基礎
類產生多個實例對象(P516)
在python中,實例從類中繼承,而類繼承於超類。
1、超類列在了類開頭的括號中。
2、類從其超類中繼承屬性。
3、實例會繼承所有可讀取類的屬性。
4、每個object.attribute都會開啓新的獨立搜索。
5、邏輯的修改是通過創建子類,而不是修改超類。
這種搜索的結果和主要目的就是,類支持了程序的分解和定製。