Python 簡易手冊

單元 37 - 二進制序列

二進制序列 (binary sequence) 包括 bytesbytearraymemoryviewbytes 是資料儲存在電腦中的原始格式,也就是數字,主要用途是網路傳輸,因為直接用數字傳輸資料更快更方便,所以傳送端先轉換編碼為 bytes ,然後接收端再轉換回所需要的資料型態 (data type) 。 bytes 是不可變的 (immutable) 序列 (sequence) ,不可變的序列支援以下操作

操作說明
x in s判斷 x 是否是 s 的元素
x not in s判斷 x 是否不是 s 的元素
s + t回傳合併 s 與 t 的新序列
s * n
n * s
回傳將 s 複製 n 倍的新序列
s[i]取得 s 中索引值為 i 的元素
s[i:j]取得 s 中索引值為 i 到 j - 1 的子序列
s[i:j:k]取得 s 中索引值為 i 到 j - 1 間隔 k 的子序列
len(s)回傳 s 中的元素總量
min(s)回傳 s 中的最小值
max(s)回傳 s 中的最大值
bytes([source[, encoding[, errors]]])將 source 轉換成 bytes 物件
bytearray([source[, encoding[, errors]]])將 source 轉換成 bytearray 物件
s.index(x[, i[, j]])回傳 s 中 x 的索引值,或是 x 在 i 到 j 之間的索引值
s.count(x)回傳 x 在 s 中的數量

以下為簡單例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 建立字串變數
a = "測試"
print(a)

# 將字串變數轉換成 bytes 物件
b = bytes(a, "utf-8")
print(b)

# 假設經過網路傳輸,以下為接收端
c = b.decode("utf-8", "strict")

# 印出解碼結果
print(c)

# 檔名: byte_demo.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 5 月

第 2 行建立字串 (string) 變數 (variable) a

 1
 2
 3
# 建立字串變數
a = "測試"
print(a)

然後在第 6 行用內建函數 (built-in function) bytes()a 轉換成 bytes 型態的變數 bbytes() 的第一個引數 (argument) 是要轉換的字串,第二個引數則是編碼格式

 5
 6
 7
# 將字串變數轉換成 bytes 物件
b = bytes(a, "utf-8")
print(b)

第 10 行,重新將變數 b 解碼回字串

 9
10
# 假設經過網路傳輸,以下為接收端
c = b.decode("utf-8", "strict")

執行結果如下

> python byte_demo.py
測試
b'\xe6\xb8\xac\xe8\xa9\xa6'
測試
>

可以看到以上輸出的第 2 行, bytes 型態是 b 開頭的引號,這也是 bytes 型態的字面常數 (literal) ,至於 \x 表示 16 進位數字,所以一個中文字在電腦中的原始格式需要三個 16 進位數字,另外 bytes 的字面常數裡頭也只能用 ASCII 字元,確保 bytes 都是數字。

bytearray 是相對 bytes 的可變 (mutable) 序列,增加支援以下操作

操作說明
s[i] = x將 s 中索引值為 i 的元素設定為 x
s[i:j] = t將 s 中索引值 i 到 j - 1 的元素替換成 t 的元素
del s[i:j]刪除 s 中 i 到 j - 1 的元素
s[i:j:k] = t將 s 中索引值 i 到 j - 1 間隔 k 的元素替換成 t 的元素
del s[i:j:k]刪除 s 中索引值 i 到 j - 1 間隔 k 的元素
s.append(x)將 x 附加到 s 的最後
s.clear()刪除 s 中的所有元素
s.copy()回傳 s 的拷貝
s.extend(t)
s += t
合併 s 與 t
s *= n將 s 複製 n 倍
s.insert(i, x)將 x 插入 s 中索引值為 i 的位置
s.pop()
s.pop(i)
移除 s 的最後一個元素或是索引值為 i 的元素
s.remove(x)移除 s 中的第一個 x
s.reverse()反轉 s 中元素的順序

舉簡單例子如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 建立 bytearray 物件
a = bytearray("測試", encoding="utf-8")
b = bytes("一下", encoding="utf-8")

# 進行連接
a += b

# 解碼並印出
c = a.decode("utf-8", "strict")
print(c)

# 檔名: byte_demo2.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 5 月

這個例子先建立 bytearray 的變數 a ,再建立 bytes 型態的變數 b ,然後用 +=b 附加到 a 的後面,此例執行結果如下

> python byte_demo2.py
測試一下
>

+= 是算術運算子 (arithmetic operator) 跟指派運算子 (assignment operator) 合在一起用,但是可以作為序列連接,單元 12 - 算術運算子介紹算術運算子,單元 14 - 指派陳述與指派運算式介紹指派運算子。

最後 memoryview 是支援緩衝協議 (buffer protocol) 的序列,特點是可以大幅加速子序列處理速度,舉例如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 從標準程式庫引入 time
import time

# 設定次數
times = []
for i in range(100000, 500001, 100000):
    times.append(i)

# 計算並印出 bytes 的處理時間
for n in times:
    data = b't' * n
    start = time.time()
    while data:
        data = data[1:]
    s = time.time() - start
    print(f'     bytes: {n} {s:0.3f}')

# 計算並印出 memoryview 的處理時間
for n in times:
    data = b'x'*n
    start = time.time()
    b = memoryview(data)
    while b:
        b = b[1:]
    s = time.time() - start
    print(f'memoryview: {n} {s:0.3f}')

# 檔名: byte_demo3.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 5 月

這個例子利用標準程式庫 (standard library) 中的 time 分別計算比較單純 bytes 及用 memoryview 處理子序列的時間,總共計算十萬到五十萬等次數,執行結果如下

> python byte_demo3.py
     bytes: 100000 0.231
     bytes: 200000 0.506
     bytes: 300000 0.924
     bytes: 400000 1.508
     bytes: 500000 2.382
memoryview: 100000 0.007
memoryview: 200000 0.015
memoryview: 300000 0.022
memoryview: 400000 0.029
memoryview: 500000 0.037
>

需要稍等一下才跑完程式,由結果可以看出 memoryview 的速度快很多,原因是序列預設取得子序列需要完整拷貝整個序列,而 memoryview 並沒有完整拷貝,所以處理速度快很多。

參考資料

上一頁 單元 36 - Unicode
回 Python 簡易手冊 首頁
下一頁 單元 38 - 序對與 Range
回 Python 教材首頁
回程式語言教材首頁