Python核心數據類型——元組
元組對象(tuple)是序列,它具有不可改變性,和字符串類似。從語法上講,它們便在圓括號中,它們支持任意類型、任意嵌套及常見的序列操作。
- 任意對象的有序集合:與字符串和列表類似,元組是一個位置有序的對象集合(內容維持從左到右的順序),可以嵌入到任何類別的對象中。
- 通過偏移存取:同字符串、列表一樣,在元組中的元素通過偏移(而不是鍵)來訪問。支持基於偏移的操作。如索引和分片。
- 屬於不可變序列類型:類似字符串,元組是不可變的,它們不支持應用在列表中任何原處修改的操作。
- 固定長度、異構、任何嵌套:元組不可變,在不生成一個拷貝的情況下不能增長或縮短。元組可以包含其他的復合對象(例如:列表、字典和其他元組等),支持嵌套。
- 對象引用的數組:元組最好看做是對象引用的數組。元組存儲指向其他對象的存取點(引用),並且對元組進行索引操作的速度相對較快。
常見元組常量和運算
運算 | 解釋 |
() | 空元組 |
T = (0,) | 單個元素的元組(非表達式) |
T = (0,’NI’,1.2,3) | 四個元素的元組,混合類型 |
T = 0,’Ni’,1.2,3 | 四個元素元組,與前列相同 |
T = (‘abs’,(‘def’,’ghi’)) | 嵌套元組 |
T = tuple(‘spam’) | 一個可迭代對象的項轉換為元組 |
T[i],T[i][j],T[i:j],len(T) | 索引,索引的索引,分片,長度 |
T1+T2,T * 3 | 合並,重復(元素項重復) |
for x in T : print(x) | 迭代 |
‘spam’ in T | 成員關系 |
[x ** 2 for x in T] | 列表解析 |
T.index(‘Ni’) | 搜索 |
T.count(‘Ni’) | 計數 |
sorted(T) | 返回一個排序后的列表 |
>>> T = (1,2,3,4) >>> len(T) 4 >>> T + (5,6) (1, 2, 3, 4, 5, 6) >>> T[0] 1 >>> T.index(4) 3 >>> T.count(4) 1 >>> T[0] = 2 #元組是不可變的 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> T = ('spam',3.0,[11,22,33]) >>> T[1] 3.0 >>> T[2][1] 22 >>> T.append(4) #不可增長或縮短 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'tuple' object has no attribute 'append'
元組確實支持字符串和列表的一般序列操作
>>> (1,2) + (3,4) #生成新的元組 (1, 2, 3, 4) >>> (1,2) * 4 #生成新的元組 (1, 2, 1, 2, 1, 2, 1, 2) >>> T = (1,2,3,4) >>> T[0],T[1:3] #生成新的元組 (1, (2, 3))
Python核心對象類型——文件
文件對象是Python代碼計算機上外部文件的主要接口。雖然文件是核心類型,但它有些特殊:沒有特定的常量語法創建文件。要創建一個文件對象,需要調用內置的open函數以字符串的形式傳遞給它一個外部的文件名以及一個處理模式的字符串。例如:創建一個文本輸出文件,可以傳遞其文件名以及’w’處理模式字符串以寫數據:
>>> f = open('data.txt','w') #當前文件夾下創建新文件 >>> f.write('Hello\n') #把hello字符串寫入文件 6 >>> f.write('world\n') 6 >>> f.close() #關閉文件
在當前文件夾下創建了一個文件,並向它寫入文本(文件名可以是完整的路徑)。讀取內容,以‘r’處理模式打開文件。
>>> f.open('data.txt','r') >>> text = f.read() >>> text 'Hello\nworld\n' >>> print(text) Hello world >>> text.split() #文件內容為字符串 ['Hello', 'world']
常見文件運算
操作 | 解釋 |
output = open(r’c:\spam',’w’) | 創建輸出文件(‘w’是指寫入) |
input = open(‘data’,’r’) | 創建輸入文件(‘r’是指讀取) |
input = open(‘data’) | 與上一行相同(‘r’是默認值) |
aString = input.read() | 把整個文件讀取到變量 |
aStrint = input.read(N) | 讀取之后的N個字節 |
aString = input.readline() | 讀取下一行(包括末標識符) |
aList = input.readlines() | 讀取整個文件到字符串列表 |
output.write(aString) | 寫入字節字符到文件 |
output.writelines(aList) | 把列表內所有字符串寫入文件 |
output.close() | 手動關閉文件(當文件手機完成時會替你關閉文件) |
output.flush() | 把輸出緩沖區刷到硬盤中,但不關閉文件 |
anyFile.seek(N) | 修改文件位置到偏移量N出以便進行下一個操作 |
for line in open(‘data’):use line | 文件迭代器一行一行地讀取 |
open(‘f.txt’,encoding=’latin-1’) | Unicode文本文件(str字符串) |
open(‘f.bin’,’rb’) | 二進制byte文件(bytes字符串) |
打開文件
為了打開一個文件,程序會調用內置函數open函數,首先是外部名,接着是處理模式。
語法:
文件句柄 = open(‘文件路徑+文件名’,’處理模式’) 如: >>> fp = open(r'c:\data\user_data.txt','r') #以只讀模式打開c:\data\user_data.txt文件
文件按指定模式打開后,通過文件句柄(如:fp)對文件內容進行操作。
處理模式典型地用字符串
- ’r’代表為只讀打開文件(默認值)。
- ’w’代表為只寫模式【不可讀;當文件不存時,創建文件;當文件存在時,清空文件內容后再寫】。
- ’a’代表為在文件尾部追加內容模式【可追加讀寫;當文件不存在時,創建后寫內容;當文件存在時,在文件內容尾部追加內容】。
處理模式也可以指定為其他選項:
- 在模式字符串尾部加上b(byte)可以進行二進制數據處理(行末轉換Unicode被關閉了),讀取到的內容是字節類型,寫入時也需要提供字節類型。
- ‘rb’
- ‘wb’
- ‘ab’
- 加上“+”意味着同時為輸入和輸出打開文件(也就是說,我們可以對相同文件對象進行讀寫,往往與文件中的修改的查找操作配合使用)
- ‘r+’:讀寫【可讀,可寫;注意不會創建不存在的文件;如果直接寫文件,則從頂部開始寫,覆蓋之前此位置的內容;如果先讀后寫,則會在文件最后追加內容。】
- ‘w+’:可寫讀。當文件不存時,創建文件;當文件存在時,覆蓋文件后再寫;注意先覆蓋文件(也就是空文件),后面使用讀無意義了】
- ‘a+’:追加寫讀。從文件頂部讀取內容,從文件底部添加內容,文件不存在則創建。
要打開的兩個參數必須都是Python的字符串,第三個是可選參數,它能夠用來控制輸出緩存:傳入“0”意味着輸出無緩存(寫入方法調用時立即傳給外部文件)。外部文件參量可能包含平台特定的以及絕對或相對目錄路徑前綴。沒有目錄路勁時,表示存在當前的工作目錄中(也就是腳本運行的地方)。
使用文件
一旦存在一個文件對象,就可以調用其方法來讀寫相關的外部文件。在任何情況下,Python程序中的文本文件都采用字符串的形式。讀取文件是會返回字符串形式的文本,文本作為字符串傳遞給write方法。
- 文件迭代器是最好的讀取性工具:雖然表中的讀寫方法都是常用的,但是要記住,現在從文本文件讀取文字行的最佳方式是根本不要讀取該文件。文件也有個迭代器會自動地在for循環,列表解析或者其他迭代語句中對文件進行逐行讀取。
- 內容是字符串,不是對象:注意從文件讀取的數據回到腳本時是一個字符串。如果字符串不是所需要的,就得將其轉換成其他類型的對象。與print不同的是,當把數據寫入文件時,Python不會自動把對象轉換為字符串——必須傳遞一個已經格式化的字符串。
- close是通常選項:調用close方法將會終止對外部文件的連接。在Python中。一旦對象不再被引用,則這個對象的內存空間就會自動被收回。當文件對象被收回的時候,Python也會自動關閉該文件。這就意味着不需要總是手動去關閉文件,尤其是對於不會運行很長時間的簡單腳本,另一方面,手動關閉調用沒有任何壞處,而且在大型程序中通常是個很不錯的習慣。嚴格的講,文件的這個手機完成后自動關閉的特性不是語言定義的一部分,而且可能隨時間而改變,因此,手動進行文件close方法調用是我們需要養成的一個好習慣。
- 文件是緩沖的並且是可查找的:關於關閉文件的提示很重要,因為關閉既釋放了操作系統資源也清空了緩沖區。默認情況下,輸出文件總是緩沖的,這意味着寫入的文本可能不會立即自動從內存轉換到硬盤——關閉一個文件,或者運行其flush方法,迫使緩沖的數據進入硬盤。可以有額外的open參數來避免緩存,但是,這可能會影響到性能。Python文件也是在字節偏移的基礎上隨機訪問的,他們的seek方法允許腳本跳轉到指定位置讀取或寫入。
簡單列子:首先為輸出而打開一個新文件,寫入一個字符串(以終止符\n結束),之后關閉文件。接下來,我們將會在輸入模式下再一次打開同一文件,讀取該行。注意第二個readline調用返回一個空字符串。這是Python文件方法告訴我們已經到達文件底部(文件的空行是含有新行符的字符串,而不是空字符串):
>>> myfile = open('myfile.txt','w') >>> myfile.write('hello textfile\n') 15 >>> myfile.write('goodbye text file\n') 18 >>> myfile.close() >>> myfile = open('myfile.txt') >>> myfile.readline() 'hello textfile\n' >>> myfile.readline() 'goodbye text file\n' >>> myfile.readline() ''
>>> open('myfile.txt').read() #一次性全部讀取整個文件 'hello textfile\ngoodbye text file\n' >>> print(open('myfile.txt').read()) #友好的顯示 hello textfile goodbye text file #如果想要一行一行地掃描一個文本文件,文件迭代器往往是最佳的選擇 >>> for line in open('myfile.txt'): ... print(line,end='') ... hello textfile goodbye text file
python3.0+中文本和二進制文件
文件類型都有open的第二個參數決定,模式字符串包含一個“b”表示二進制。python總是支持文本和二進制文件。二者之間的明顯區別:
- 文本文件內容表示為常規的str字符串,自動執行Unicode(包括ACSII和其他的8位編碼)編碼和解碼,並且默認執行末行轉換。
- 二進制文件把內容表示一個特殊的bytes字符串類型,並且允許程序不修改地訪問文件內容。
>>> data = open('data.bin','wb') >>> data.write(b'\x00\x00\x00\x07spam\x00\x08') 10 >>> data.close() >>> data = open('data.bin','rb').read() >>> data b'\x00\x00\x00\x07spam\x00\x08' >>> data[4:8] b'spam' >>> data[4] 115 >>> bin(data[4]) '0b1110011'
此外,二進制文件不會對數據執行任何末行轉換;在根據轉化寫入和讀取並實現Unicode編碼的時候,文本文件默認地把所有形式和\n之間映射起來。
在文件中存儲並解析python對象
需要注意的是,我們必須使用轉換工具把對象轉成字符串保存至文件中,寫入方法不會自動地替我們做任何字符串格式轉換工作。
>>> x,y,z = 43,44,45 >>> s = 'spam' #字符串可以直接存至文件 >>> D = {'a':1,'b':2} >>> L = [1,2,3] >>> F = open('datafile.txt','w') >>> F.write(s + '\n') #添加換行符 5 >>> F.write('{0},{1},{2}\n'.format(x,y,z)) #需要轉換為字符串 9 >>> F.write(str(L) + '$' + str(D) + '\n') #需要轉換為字符串 27 >>> F.close()
通過打開和讀取文件查看內容返回的為字符串。使用print語句則會解釋嵌行終止符顯示友好結果:
>>> chars = open('datafile.txt').read() >>> chars "spam\n43,44,45\n[1, 2, 3]${'b': 2, 'a': 1}\n" >>> print(chars) spam 43,44,45 [1, 2, 3]${'b': 2, 'a': 1}
在真正的使用用過程中,讀取文件后python不會自動把字符串轉換為數字或其他類型的對象。我們不得不使用其他轉換工具,把文本文件中的字符串轉成真正的python對象。
>>> F = open('datafile.txt') >>> line = F.readline() >>> line 'spam\n' >>> line.rstrip() #用rstrip方法去掉多余的終止符 'spam' >>> line 'spam\n' >>> line[:-1]#或用分片去掉多余的終止符 'spam' >>> line = F.readline() >>> line '43,44,45\n' >>> parts = line.split(',') #用split方法從逗號中轉換為列表 >>> parts ['43', '44', '45\n'] >>> int(parts[1]) 44 >>> parts = [int(p) for p in parts] #需要得到真正的整數類型,需要int函數轉換 >>> parts [43, 44, 45] #還有一個方法可以用內置函數eval進行轉換。eval函數能夠把字符串當做可執行程序代碼(從技術上講,就是一個含有python表達式的字符串)。 >>> line = F.readline() >>> line "[1, 2, 3]${'b': 2, 'a': 1}\n" >>> parts = line.split('$') #split從$中轉換為列表 >>> parts ['[1, 2, 3]', "{'b': 2, 'a': 1}\n"] >>> eval(parts[0]) #eval函數可以把字符串轉換為可執行代碼 [1, 2, 3] >>> objects = [eval(p) for p in parts] >>> objects [[1, 2, 3], {'b': 2, 'a': 1}]
文件方法:
方法 | 描述 |
fileObject.close() | close() 方法用於關閉一個已打開的文件。關閉后的文件不能再進行讀寫操作, 否則會觸發 ValueError 錯誤。 close() 方法允許調用多次。當 file 對象,被引用到操作另外一個文件時,Python 會自動關閉之前的 file 對象。 使用 close() 方法關閉文件是一個好的習慣。 |
fileObject.flush() | flush() 方法是用來刷新緩沖區的,即將緩沖區中的數據立刻寫入文件,同時清空緩沖區,不需要是被動的等待輸出緩沖區寫入。一般情況下,文件關閉后會自動刷新緩沖區,但有時你需要在關閉前刷新它,這時就可以使用 flush() 方法。 |
fileObject.fileno() |
fileno() 方法返回一個整型的文件描述符(file descriptor FD 整型),可用於底層操作系統的 I/O 操作。 |
fileObject.isatty() |
isatty() 方法檢測文件是否連接到一個終端設備,如果是返回 True,否則返回 False。 |
fileObject.next() |
next() 方法在文件使用迭代器時會使用到,在循環中,next()方法會在每次循環中調用,該方法返回文件的下一行,如果到達結尾(EOF),則觸發 StopIteration |
fileObject.read([size]) |
read([size]) ,size為數值,方法用於從文件讀取指定的字符數【無b模式時】或字節數【加上b模式時】,如果size未給定或為負則讀取所有。 |
fileObject.readline() |
readline() 方法用於從文件讀取整行,包括 "\n" 字符。如果指定了一個非負數的參數,則返回指定大小的字節數,包括 "\n" 字符。 |
fileObject.readlines( sizehint ) |
readlines() 方法用於讀取所有行(直到結束符 EOF)並返回列表,若給定sizeint>0,返回總和大約為sizeint字節的行, 實際讀取值可能比sizhint較大, 因為需要填充緩沖區。如果碰到結束符 EOF 則返回空字符串。sizehint -- 從文件中讀取的字節數/字符數。 |
fileObject.seek(offset[, whence]) |
seek() 方法用於移動文件讀取指針到指定位置。offset -- 開始的偏移量,也就是代表需要移動偏移的字節數。whence:可選,默認值為 0。給offset參數一個定義,表示要從哪個位置開始偏移;0代表從文件開頭開始算起,1代表從當前位置開始算起,2代表從文件末尾算起。 |
fileObject.tell(offset[, whence]) |
tell() 方法返回文件的當前位置,即文件指針當前位置。 |
fileObject.truncate( [ size ]) |
truncate() 方法用於截斷文件,如果指定了可選參數 size,則表示截斷文件為 size 個字符。 如果沒有指定 size,則從當前位置起截斷;截斷之后 size 后面的所有字符被刪除。size -- 可選,如果存在則文件截斷為 size 字節。 |
fileObject.write( [ str ]) |
write() 方法用於向文件中寫入指定字符串。在文件關閉前或緩沖區刷新前,字符串內容存儲在緩沖區中,這時你在文件中是看不到寫入的內容的。str -- 要寫入文件的字符串。 |
fileObject.writelines( [ str ]) |
writelines() 方法用於向文件中寫入一序列的字符串。這一序列字符串可以是由迭代對象產生的,如一個字符串列表。換行需要制定換行符 \n。str -- 要寫入文件的字符串序列。 |
用pickle存儲Python的原生對象
使用eval可以把字符串轉換為對象,它是一個功能強大的工具,有時它太過於強大,只要權限足夠,會執行python的任何表達式,甚至有可能會刪除計算機上的所有文件的表達式。
如果真的要存儲python原生對象,又無法信賴文件的數據來源,python標准庫pickle模塊會是個理想的選擇。
pickle模塊幾乎能夠存儲任何python對象的高級工具,不用我們把字符串轉換來轉換去,也就是我們可以直接存儲任何對象,從文件中取回時,仍然是之前保存時的對象。實際上,pickle是內部將對像轉換為字符串形式,取回時pick自動重建對象。
>>> D = {'a':1,'b':2} >>> F = open('datafile.pkl','wb') >>> import pickle >>> pickle.dump(D,F) >>> F.close() >>> F = open('datafile.pkl','rb') >>> E = pickle.load(F) >>> E {'b': 2, 'a': 1}
注意:pickle程序創建和使用一個bytes字符串對象,意味着這些對象創建的是二進制模式文件。所以我們以二進制模式打開用來存儲pickle化的對象的文件。
文件中打包位二進制數據的存儲與解析
有些高級應用程序需要處理打包的二進制數據,這些數據可能是C語言程序生成的。Python標准庫中包含一個能夠在一范圍起作用的工具:struct模塊能夠構造並解析打包的二進制數據。從某種意義上說,它是另一個數據轉換工具,能夠把文件中的字符串讀為二進制數據。
例如:要生成一個打包的二進制數據文件,用‘wb’(寫入二進制)模式打開,並將一個格式化字符串和幾個python對象傳給struct。這里用的格式化字符串是指一個4字節整數、一個包含4個字符的字符串以及一個2位整數的數據包,所有這些都按照高位在前(big-endian)的形式(其他格式代碼能夠處理補位字節、浮點數)。
##struct打包 >>> F = open('data.bin','wb') >>> import struct >>> data = struct.pack('>i4sh',7,b'spam',8) #注意字符串必須是二進制b模式 >>> data b'\x00\x00\x00\x07spam\x00\x08' >>> F.write(data) 10 >>> F.close() ##struct解包 >>> F = open('data.bin','rb') >>> data = F.read() >>> data b'\x00\x00\x00\x07spam\x00\x08' >>> values = struct.unpack('>i4sh',data) >>> values (7, b'spam', 8)
詳情可以參考python庫手冊,或交互模式下dir和help函數查看
>>> dir(struct) ['Struct', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__lo ader__', '__name__', '__package__', '__spec__', '_clearcache', 'calcsize', 'erro r', 'iter_unpack', 'pack', 'pack_into', 'unpack', 'unpack_from']
文件上下文管理器
文件上下文管理比文件自身多了一個異常處理功能,它允許我們把文件處理代碼包裝到一個邏輯層中,以確保在退出后可以自動關閉文件,而不是依賴於垃圾收集上的自動關閉:
with open(r'c:\misc\data.txt') as fp: for line1 in fp: "...use line here..."
python2.7及以后版本,還可以同時打開多個文件:
with open(r'c:\misc\data1.txt') as fp1 , open(r"c:\misc\data1.txt") as fp2,.....: for line1 in fp1: "...use line here..." for line2 in fp2: "...use line here..."
其他文件類工具
open函數能夠實現在Python中編寫的絕大多數文件處理。盡管更高級的任務,Python還有額外的類文件工具:管道、先進先出隊列(FIFO)、套接字、通過鍵訪問文件、對象持久、基於描述符的文件、關系數據庫和面向對象數據庫接口等。例如,描述符文件(descriptor file) 支持文件鎖定和其他的底層工具,而套接字提供網絡和進程間通信的接口。
- 標准流:在sys模塊中預先打開的文件對象,例如:sys.stout
- os模塊中的描述文件:處理整數文件,支持諸如文件鎖定之類的較低級工具。
- socket、pipes和FIFO文件:文件類對象,用於同步進程或者通過網絡進行通信。
- 通過鍵來存取的文件:通過鍵直接存儲的不變的python對象。
- shell命令流:像os.popen和subprocess.Popen這樣的工具,支持產生shell命令,並讀取和寫入到標准流。
檢查代碼對象類型
Python腳本中至少有3種方法可以做到代碼對象類型檢測:
>>> if type(L) == type([]): print('yes') ... yes >>> if type(L) == list: print('yes') ... yes >>> if isinstance(L,list): print('yes') ... yes