io --- 輸入/輸出串流

本模組包含其他類型的 stream(類檔案)物件以及輔助函式。它公開了內建的 open() 函式,並提供記憶體中的文字與二進位緩衝區(StringIOBytesIO),這些緩衝區實作了標準的 read/write/seek 串流介面。

概念階層

與 CPython 的差異

串流基底類別的概念階層在 MicroPython 中經過簡化,如本節所述。

(抽象)基底串流類別作為所有具體類別行為的基礎,在 CPython 中遵循少數幾項二分法(成對分類)。在 MicroPython 中,這些分類經過某種程度的簡化,並隱含於實作之中,以達到更高的效率並節省資源。

CPython 中一項重要的二分法是無緩衝串流與有緩衝串流。在 MicroPython 中,目前所有串流皆為無緩衝。這是因為所有現代作業系統,甚至許多 RTOS 與檔案系統驅動程式都已在其端進行緩衝。再加入一層緩衝會適得其反(這個問題稱為「緩衝膨脹」),且會佔用寶貴的記憶體。請注意,在某些情況下緩衝仍可能有用,因此我們日後可能會引入選用的緩衝支援。

但在 CPython 中,另一項重要的二分法與「是否有緩衝」息息相關——即串流是否可能發生短讀/短寫。所謂短讀,是指使用者向串流要求例如 10 個位元組,卻得到較少的位元組;短寫亦同理。在 CPython 中,無緩衝串流會自動容易發生短操作,而有緩衝串流則保證不會發生。不發生短讀/短寫是一項重要特性,因為它能讓開發者撰寫更精簡且高效的程式——這正是 MicroPython 高度期望的目標。因此,雖然 MicroPython 不支援有緩衝串流,但它仍提供不發生短操作的串流。是否會發生短操作取決於每個特定類別的需求,但強烈建議開發者基於上述理由優先採用不發生短操作的行為。例如,MicroPython 的 socket 保證避免短讀/短寫。實際上目前核心中並沒有短操作串流類別的範例,而這類類別會是特定於某種硬體的。

在非阻塞串流的情況下,不發生短操作的行為會變得棘手;阻塞與非阻塞行為是 CPython 的另一項二分法,且 MicroPython 完全支援。非阻塞串流絕不會等待資料抵達或寫入——它們會盡可能讀取/寫入,或在缺乏資料(或無法寫入資料)時發出訊號。顯然這與「不發生短操作」的原則相衝突,事實上,非阻塞且有緩衝(亦即不發生短操作)串流的情況在 CPython 中相當複雜——有些地方禁止這種組合,有些地方則行為未定義或單純未加說明,某些情況下還會擲出冗長的例外。這件事在 MicroPython 中簡單得多:非阻塞串流對於高效的非同步操作至關重要,因此此特性優先於「不發生短操作」特性。所以,雖然阻塞串流會盡可能避免短讀/短寫(唯一會發生短讀的情況是抵達檔案結尾,或發生錯誤時(但錯誤不會回傳短資料,而是擲出例外)),非阻塞串流則可能產生短資料以避免阻塞該操作。

最後一項二分法是二進位串流與文字串流。MicroPython 當然支援這兩者,但在 CPython 中文字串流本質上是有緩衝的,在 MicroPython 中則並非如此。(事實上,這正是我們可能引入緩衝支援的情況之一。)

請注意,為了效率,MicroPython 並未提供對應於上述階層的抽象基底類別,而且無法在純 Python 中實作或繼承串流類別。

函式

io.open(name: str, mode: str = 'r', **kwargs) Any

開啟檔案。內建的 open() 函式即為此函式的別名。一律支援 mode 參數;其他引數的支援程度可能有所不同。

類別

class io.IOBase

串流(「類檔案」)物件的基底類別。具體的子類別會實作下方的低階 I/O 方法(readintowriteioctl);執行階段會在其上建構較高階的串流協定(readreadlinereadlinesclose、迭代),因此即使子類別未定義這些方法,每個串流實例也都支援它們。

實作方法(在子類別中覆寫這些方法):

readinto(buf: bytearray) int | None

將位元組讀入可寫入的緩衝區 buf。回傳讀取的位元組數、在串流結尾時回傳 0,若目前沒有可用資料則回傳 None(適用於非阻塞串流)。

write(buf: bytes) int | None

寫入 buf 中的位元組。回傳寫入的位元組數,若目前無法執行寫入則回傳 None(適用於非阻塞串流)。

ioctl(request: int, arg: int) int

控制底層的串流/裝置。requestMP_STREAM_* 請求碼之一。成功時回傳非負值,發生錯誤時回傳負的 errno 值。

串流協定方法(可用於每個串流實例):

read(size: int = -1)

讀取並回傳最多 size 個位元組(在文字模式下則為字元)。若省略 size 或為負值,則讀取至串流結尾。二進位串流回傳 bytes,文字串流回傳 str;空的結果表示串流結尾。

readline(size: int = -1)

讀取並回傳一行,若存在則包含尾端的換行字元。若有指定 size,則最多讀取 size 個位元組(或字元)。在串流結尾時回傳空的 bytes / str

readlines() list

讀取至串流結尾,並回傳由各行組成的 list,每行皆含其尾端換行字元。

close() None

關閉串流並釋放任何底層資源。對已關閉的串流進行操作會擲出 OSError(記憶體中串流則擲出 ValueError)。

seek(offset: int, whence: int = 0) int

將目前的串流位置變更為相對於 whenceoffset 位元組(0 = 串流起點,1 = 目前位置,2 = 串流結尾)。回傳新的絕對位置。在無法尋址的串流上會擲出 OSError

tell() int

回傳串流中目前的絕對位置。等同於 seek(0, 1)

flush() None

排清任何寫入緩衝區,將待處理的資料推送至底層裝置或檔案。對不進行緩衝的串流而言此為無操作。

直接迭代串流時,每次迭代會產生一行——等同於在迴圈中呼叫 readline(),直到回傳代表串流結尾的空行哨符為止。串流也支援情境管理器協定,因此 with open(...) as f: 會自動關閉串流。

備註

MicroPython 的串流模組還公開了帶有「1」字尾的 C 輔助函式 mp_stream_read1_objmp_stream_readinto1_objmp_stream_write1_obj,它們會執行單一次底層 I/O 呼叫,而非反覆迴圈直到請求完全滿足為止。這些函式由像 machine.UART 之類的類別在內部使用,以實作其自身的 read / write——但沒有任何標準串流類別將它們繫結為可由 Python 呼叫的 read1 / readinto1 / write1 方法。

class io.StringIO(string: str = '')

用於文字模式輸入/輸出的記憶體中類檔案物件(類似以「t」修飾符開啟的一般檔案)。可使用 string 參數指定初始內容(該參數應為一般字串)。實例也支援情境管理器協定(可用於 with 陳述式中)。

read(size: int = -1) str

讀取並回傳最多 size 個字元。若省略 size 或為負值,則讀取並回傳所有剩餘內容。

readline(size: int = -1) str

讀取並回傳一行。若有指定 size,則最多讀取 size 個字元。

readinto(buf: bytearray) int

讀入預先配置好的可寫入緩衝區 buf,並回傳讀取的位元組數。

write(s: str) int

寫入字串 s 並回傳寫入的字元數。

seek(offset: int, whence: int = 0) int

將串流位置變更為相對於 whenceoffset0 = 起點,1 = 目前位置,2 = 結尾),並回傳新的絕對位置。

tell() int

回傳目前的串流位置。

flush() None

排清寫入緩衝區。對記憶體中串流而言此為無操作。

close() None

關閉串流並釋放底層緩衝區。對已關閉的串流進行進一步操作會擲出 ValueError

getvalue() str

回傳底層緩衝區目前的內容。

class io.StringIO(alloc_size: int)

建立一個空的 StringIO 物件,預先配置可容納最多 alloc_size 個位元組的空間,如此一來,寫入至多這麼多位元組時就不會重新配置緩衝區(避免記憶體不足的情況或記憶體碎片化)。此建構式是 MicroPython 的擴充功能,僅建議用於特殊情況與系統層級的函式庫,而非最終使用者的應用程式。

與 CPython 的差異

此建構式是 MicroPython 的擴充功能。

read(size: int = -1) str

讀取並回傳最多 size 個字元。若省略 size 或為負值,則讀取並回傳所有剩餘內容。

readline(size: int = -1) str

讀取並回傳一行。若有指定 size,則最多讀取 size 個字元。

readinto(buf: bytearray) int

讀入預先配置好的可寫入緩衝區 buf,並回傳讀取的位元組數。

write(s: str) int

寫入字串 s 並回傳寫入的字元數。

seek(offset: int, whence: int = 0) int

將串流位置變更為相對於 whenceoffset0 = 起點,1 = 目前位置,2 = 結尾),並回傳新的絕對位置。

tell() int

回傳目前的串流位置。

flush() None

排清寫入緩衝區。對記憶體中串流而言此為無操作。

close() None

關閉串流並釋放底層緩衝區。對已關閉的串流進行進一步操作會擲出 ValueError

getvalue() str

回傳底層緩衝區目前的內容。

class io.BytesIO(string: bytes = b'')

用於二進位模式輸入/輸出的記憶體中類檔案物件(類似以「b」修飾符開啟的一般檔案)。可使用 string 參數指定初始內容(該參數應為 bytes 物件)。實例也支援情境管理器協定(可用於 with 陳述式中)。

read(size: int = -1) bytes

讀取並回傳最多 size 個位元組。若省略 size 或為負值,則讀取並回傳所有剩餘內容。

readline(size: int = -1) bytes

讀取並回傳一行。若有指定 size,則最多讀取 size 個位元組。

readinto(buf: bytearray) int

讀入預先配置好的可寫入緩衝區 buf,並回傳讀取的位元組數。

write(b: bytes) int

寫入類位元組物件 b 並回傳寫入的位元組數。

seek(offset: int, whence: int = 0) int

將串流位置變更為相對於 whenceoffset0 = 起點,1 = 目前位置,2 = 結尾),並回傳新的絕對位置。

tell() int

回傳目前的串流位置。

flush() None

排清寫入緩衝區。對記憶體中串流而言此為無操作。

close() None

關閉串流並釋放底層緩衝區。對已關閉的串流進行進一步操作會擲出 ValueError

getvalue() bytes

回傳底層緩衝區目前的內容。

class io.BytesIO(alloc_size: int)

建立一個空的 BytesIO 物件,預先配置可容納最多 alloc_size 個位元組的空間,如此一來,寫入至多這麼多位元組時就不會重新配置緩衝區(避免記憶體不足的情況或記憶體碎片化)。此建構式是 MicroPython 的擴充功能,僅建議用於特殊情況與系統層級的函式庫,而非最終使用者的應用程式。

與 CPython 的差異

此建構式是 MicroPython 的擴充功能。

read(size: int = -1) bytes

讀取並回傳最多 size 個位元組。若省略 size 或為負值,則讀取並回傳所有剩餘內容。

readline(size: int = -1) bytes

讀取並回傳一行。若有指定 size,則最多讀取 size 個位元組。

readinto(buf: bytearray) int

讀入預先配置好的可寫入緩衝區 buf,並回傳讀取的位元組數。

write(b: bytes) int

寫入類位元組物件 b 並回傳寫入的位元組數。

seek(offset: int, whence: int = 0) int

將串流位置變更為相對於 whenceoffset0 = 起點,1 = 目前位置,2 = 結尾),並回傳新的絕對位置。

tell() int

回傳目前的串流位置。

flush() None

排清寫入緩衝區。對記憶體中串流而言此為無操作。

close() None

關閉串流並釋放底層緩衝區。對已關閉的串流進行進一步操作會擲出 ValueError

getvalue() bytes

回傳底層緩衝區目前的內容。