python3-cookbook第5章摘要-檔案與IO
1.如何在迭代一個序列的同時跟蹤正在被處理的元素索引。
內建的 enumerate() 函式可以很好的解決這個問題:
>>> my_list = ['a', 'b', 'c']
>>> for idx, val in enumerate(my_list):
... print(idx, val)
...
0 a
1 b
2 c
為了按傳統行號輸出(行號從1開始),你可以傳遞一個開始引數:
>>> my_list = ['a', 'b', 'c'] >>> for idx, val in enumerate(my_list, 1): ... print(idx, val) ... 1 a 2 b 3 c
這種情況在你遍歷檔案時想在錯誤訊息中使用行號定位時候非常有用:
def parse_data(filename):
with open(filename, 'rt') as f:
for lineno, line in enumerate(f, 1):
fields = line.split()
try:
count = int(fields[1])
...
except ValueError as e:
print('Line {}: Parse error: {}' .format(lineno, e))
2.如何同時迭代多個序列,每次分別從一個序列中取一個元素。
為了同時迭代多個序列,使用 zip() 函式。比如:
>>> xpts = [1, 5, 4, 2, 10, 7]
>>> ypts = [101, 78, 37, 15, 62, 99]
>>> for x, y in zip(xpts, ypts):
... print(x,y)
...
1 101
5 78
4 37
2 15
10 62
7 99
zip(a, b) 會生成一個可返回元組 (x, y) 的迭代器,其中x來自a,y來自b。 一旦其中某個序列到底結尾,迭代宣告結束。 因此迭代長度跟引數中最短序列長度一致。
如果這個不是你想要的效果,那麼還可以使用 itertools.zip_longest() 函式來代替
>>> from itertools import zip_longest
>>> for i in zip_longest(a,b):
... print(i)
...
(1, 'w')
(2, 'x')
(3, 'y')
(None, 'z')
>>> for i in zip_longest(a, b, fillvalue=0):
... print(i)
...
(1, 'w')
(2, 'x')
(3, 'y')
(0, 'z')
>>>
雖然不常見,但是 zip() 可以接受多於兩個的序列的引數。 這時候所生成的結果元組中元素個數跟輸入序列個數一樣。
3.如何將一系列排序序列合併後得到一個排序序列並在上面迭代遍歷。
heapq.merge() 函式可以幫你解決這個問題。比如:
>>> import heapq
>>> a = [1, 4, 7, 10]
>>> b = [2, 5, 6, 11]
>>> for c in heapq.merge(a, b):
... print(c)
...
1
2
4
5
6
7
10
11
heapq.merge 可迭代特性意味著它不會立馬讀取所有序列。 這就意味著你可以在非常長的序列中使用它,而不會有太大的開銷。 比如,下面是一個例子來演示如何合併兩個排序檔案:
with open('sorted_file_1', 'rt') as file1, \
open('sorted_file_2', 'rt') as file2, \
open('merged_file', 'wt') as outf:
for line in heapq.merge(file1, file2):
outf.write(line)
有一點要強調的是 heapq.merge() 需要所有輸入序列必須是排過序的。 特別的,它並不會預先讀取所有資料到堆疊中或者預先排序,也不會對輸入做任何的排序檢測。 它僅僅是檢查所有序列的開始部分並返回最小的那個,這個過程一直會持續直到所有輸入序列中的元素都被遍歷完。
4.使用 print() 函式輸出資料,改變預設的分隔符或者行尾符。
可以使用在 print() 函式中使用 sep 和 end 關鍵字引數,以你想要的方式輸出。比如:
>>> print('ACME', 50, 91.5)
ACME 50 91.5
>>> print('ACME', 50, 91.5, sep=',')
ACME,50,91.5
>>> print('ACME', 50, 91.5, sep=',', end='!!\n')
ACME,50,91.5!!
>>>
使用 end 引數也可以在輸出中禁止換行。比如:
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
>>> for i in range(5):
... print(i, end=' ')
...
0 1 2 3 4 >>>
5. 一個檔案中寫入資料,但是前提必須是這個檔案在檔案系統上不存在。 也就是不允許覆蓋已存在的檔案內容。
可以在 open() 函式中使用 x 模式來代替 w 模式的方法來解決這個問題。比如:
>>> with open('somefile', 'wt') as f:
... f.write('Hello\n')
...
>>> with open('somefile', 'xt') as f:
... f.write('Hello\n')
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileExistsError: [Errno 17] File exists: 'somefile'
>>>
如果檔案是二進位制的,使用 xb 來代替 xt
6.如何讀寫一個gzip或bz2格式的壓縮檔案。
import gzip
with gzip.open('somefile.gz', 'rt') as f:
text = f.read()
# bz2 compression
import bz2
with bz2.open('somefile.bz2', 'rt') as f:
text = f.read()
類似的,為了寫入壓縮資料,可以這樣做:
# gzip compression
import gzip
with gzip.open('somefile.gz', 'wt') as f:
f.write(text)
# bz2 compression
import bz2
with bz2.open('somefile.bz2', 'wt') as f:
f.write(text)
當寫入壓縮資料時,可以使用 compresslevel 這個可選的關鍵字引數來指定一個壓縮級別。比如:
with gzip.open('somefile.gz', 'wt', compresslevel=5) as f:
f.write(text)
7.在一個固定長度記錄或者資料塊的集合上迭代,而不是在一個檔案中一行一行的迭代。
#通過下面這個小技巧使用 iter 和 functools.partial() 函式:
from functools import partial
RECORD_SIZE = 32
with open('somefile.data', 'rb') as f:
records = iter(partial(f.read, RECORD_SIZE), b'')
for r in records:
...
8.在程式執行時建立一個臨時檔案或目錄,並希望使用完之後可以自動銷燬掉。
from tempfile import TemporaryFile
with TemporaryFile('w+t') as f:
# Read/write to the file
f.write('Hello World\n')
f.write('Testing\n')
# Seek back to beginning and read the data
f.seek(0)
data = f.read()
# Temporary file is destroyed
或者,如果你喜歡,你還可以像這樣使用臨時檔案:
f = TemporaryFile('w+t')
# Use the temporary file
...
f.close()
# File is destroyed
在大多數Unix系統上,通過 TemporaryFile() 建立的檔案都是匿名的,甚至連目錄都沒有。 如果你想打破這個限制,可以使用 NamedTemporaryFile() 來代替。比如:
from tempfile import NamedTemporaryFile
with NamedTemporaryFile('w+t') as f:
print('filename is:', f.name)
...
# File automatically destroyed
9.將一個Python物件序列化為一個位元組流,以便將它儲存到一個檔案、儲存到資料庫或者通過網路傳輸它。
對於序列化最普遍的做法就是使用 pickle 模組。為了將一個物件儲存到一個檔案中,可以這樣做:
import pickle
data = ... # Some Python object
f = open('somefile', 'wb')
pickle.dump(data, f)
為了將一個物件轉儲為一個字串,可以使用 pickle.dumps() :
s = pickle.dumps(data)
為了從位元組流中恢復一個物件,使用 picle.load() 或 pickle.loads() 函式。比如:
# Restore from a file
f = open('somefile', 'rb')
data = pickle.load(f)
# Restore from a string
data = pickle.loads(s)