3.4. array — 序列化的固定类型结构

目的:有效的管理序列化的固定类型结构的数值型数据。

array 模块定义了一个与 list 非常相似的序列化数据结构,只是所有的成员都必须是相同的初始类型。支持的类型有:所有的数值型或者其他固定大小的初始类型,如 bytes 型。

对一些支持的类型可以参考以下表格。array 标准库的文档包含了完整的类型代码清单。

数组成员的类型代码

代码 类型 最小占用空间 (bytes)
b int 1
B int 1
h signed short 2
H unsigned short 2
i signed int 2
I unsigned int 2
l signed long 4
L unsigned long 4
q signed long long 8
Q unsigned long long 8
f float 4
d double float 8

初始化

 array 实例化时会用到一个参数,该参数描述允许的数据类型。并且在初始化时可能还要给数组赋一个初值。

array_string.py

import array
import binascii

s = b'This is the array.'
a = array.array('b', s)

print('As byte string:', s)
print('As array      :', a)
print('As hex        :', binascii.hexlify(a))

在本例中,数组被配置为保存一个字节序列,并使用一个简单的字节字符串进行初始化。

$ python3 array_string.py

As byte string: b'This is the array.'
As array      : array('b', [84, 104, 105, 115, 32, 105, 115, 32,
 116, 104, 101, 32, 97, 114, 114, 97, 121, 46])
As hex        : b'54686973206973207468652061727261792e'

操作数组

其它 Python 序列可以执行的操作, array 都可以用相同的方式执行,包括扩展数组的操作。

array_sequence.py

import array
import pprint

a = array.array('i', range(3))
print('Initial :', a)

a.extend(range(3))
print('Extended:', a)

print('Slice   :', a[2:5])

print('Iterator:')
print(list(enumerate(a)))

支持的操作包括切片、迭代和向末尾添加元素。

$ python3 array_sequence.py

Initial : array('i', [0, 1, 2])
Extended: array('i', [0, 1, 2, 0, 1, 2])
Slice   : array('i', [2, 0, 1])
Iterator:
[(0, 0), (1, 1), (2, 2), (3, 0), (4, 1), (5, 2)]

数组和文件

使用高效编码的内置方法,可以将数组的内容写入文件,也可以将文件的内容读取到数组中。

array_file.py

import array
import binascii
import tempfile

a = array.array('i', range(5))
print('A1:', a)

# 将数字数组写入临时文件
output = tempfile.NamedTemporaryFile()
a.tofile(output.file)  # 必须传递一个 *真正的* 文件
output.flush()

# 读取原始数据
with open(output.name, 'rb') as input:
    raw_data = input.read()
    print('Raw Contents:', binascii.hexlify(raw_data))

    # 将数据读取到一个数组中
    input.seek(0)
    a2 = array.array('i')
    a2.fromfile(input, len(a))
    print('A2:', a2)

这个例子演示了如何直接从一个二进制文件中读取数据「 raw, 」,而不是将其读入一个新的数组并将字节转换为适当的类型。

$ python3 array_file.py

A1: array('i', [0, 1, 2, 3, 4])
Raw Contents: b'0000000001000000020000000300000004000000'
A2: array('i', [0, 1, 2, 3, 4])

tofile() 使用 tobytes() 来格式化数据, fromfile() 使用 frombytes() 来将数据转换会数组实例。

array_tobytes.py

import array
import binascii

a = array.array('i', range(5))
print('A1:', a)

as_bytes = a.tobytes()
print('Bytes:', binascii.hexlify(as_bytes))

a2 = array.array('i')
a2.frombytes(as_bytes)
print('A2:', a2)

 tobytes() 和 frombytes() 处理的都是字节字符串,而不是 Unicode 字符串。

$ python3 array_tobytes.py

A1: array('i', [0, 1, 2, 3, 4])
Bytes: b'0000000001000000020000000300000004000000'
A2: array('i', [0, 1, 2, 3, 4])

可供选择的字节顺序

如果数组中的数据不是按照本机字节顺序排列的,或者说如果在以不同的字节顺序(或通过网络)将数据发送给系统之前需要交换数据的位置,则可以在不迭代 Python 元素的情况之下转换整个数组的排列顺序。

array_byteswap.py

import array
import binascii

def to_hex(a):
    chars_per_item = a.itemsize * 2  # 2 个16进制数字
    hex_version = binascii.hexlify(a)
    num_chunks = len(hex_version) // chars_per_item
    for i in range(num_chunks):
        start = i * chars_per_item
        end = start + chars_per_item
        yield hex_version[start:end]

start = int('0x12345678', 16)
end = start + 5
a1 = array.array('i', range(start, end))
a2 = array.array('i', range(start, end))
a2.byteswap()

fmt = '{:>12} {:>12} {:>12} {:>12}'
print(fmt.format('A1 hex', 'A1', 'A2 hex', 'A2'))
print(fmt.format('-' * 12, '-' * 12, '-' * 12, '-' * 12))
fmt = '{!r:>12} {:12} {!r:>12} {:12}'
for values in zip(to_hex(a1), a1, to_hex(a2), a2):
    print(fmt.format(*values))

 byteswap() 方法可以在 C语言 中切换数组中项的字节顺序,因此它比在 Python 中对数据进行循环要高效得多。

$ python3 array_byteswap.py

      A1 hex           A1       A2 hex           A2
------------ ------------ ------------ ------------
 b'78563412'    305419896  b'12345678'   2018915346
 b'79563412'    305419897  b'12345679'   2035692562
 b'7a563412'    305419898  b'1234567a'   2052469778
 b'7b563412'    305419899  b'1234567b'   2069246994
 b'7c563412'    305419900  b'1234567c'   2086024210

另请参阅