Python标准库array模块用法:怎么创建固定类型数值数组

夏知夏
夏知夏
发布时间:2026-11-15 | 4人浏览过
原创

详细讲解Python标准库array模块的用法,包括类型码选择、创建固定类型数值数组的多种方式、二进制文件读写以及与原生list的内存机制对比。

在 Python 中处理大量数值时,最常用的 list 虽然灵活,但每个元素都是被封装的独立对象指针,存储百万级数字时会带来显著的内存冗余与性能开销。Python 标准库自带的 array 模块提供了一种紧凑存储固定类型数值的轻量解决方案,它直接在连续内存中存放底层 C 语言原生数据,不需要额外安装 NumPy 等第三方扩展包。本文详细介绍 array 模块的核心原理、类型码选择、四种具体创建方式与实际应用中的关键细节。

一、核心原理:array 与原生 list 的内存结构差异

Python 原生列表是一个动态指针数组。当你在列表中存放 [10, 20, 30, 40] 时,列表底层保存的是指向四个独立 PyLongObject 对象的内存地址指针。每一个 Python 整数对象除数值本身外,还附带引用计数、类型指针以及长度信息,通常至少占用 28 字节。再加上指针本身的 8 字节,一个整型的实际消耗高达 36 字节以上。

而标准库的 array.array 直接在单一连续内存块中按字节紧凑存储数值,省去了所有对象封装与指针跳转。以 32 位整型为例,每个数字在 array 中严格只占 4 字节,不仅节省了大量内存,还能让 CPU 缓存更高效地命中,极大提升数据传输和读写速度。

Python标准库array模块用法:怎么创建固定类型数值数组
图 1:Python 原生 list 与 array.array 底层内存模型对比

二、核心语法与类型码(Type Code)对照表

创建 array 对象的构造语法如下:

from array import array

arr = array(typecode[, initializer])

其中,第一个参数 typecode(类型码)是单一字符,用来指定数组中每个元素所采用的底层 C 语言数据类型与字节长度。创建后,该数组只能容纳符合该类型码范围的数值,存入超出边界或不符合类型的元素会直接抛出异常。

类型码 (Type Code) 对应 C 语言类型 Python 内部映射类型 最小占用字节数 数值表示范围
'b' signed char int 1 -128 至 127
'B' unsigned char int 1 0 至 255(常用于字节流、灰度像素)
'h' signed short int 2 -32,768 至 32,767
'H' unsigned short int 2 0 至 65,535
'i' signed int int 2 (通常为4) -2,147,483,648 至 2,147,483,647
'I' unsigned int int 2 (通常为4) 0 至 4,294,967,295
'l' / 'L' signed / unsigned long int 4 (部分64位平台为8) 通常为 32 位或 64 位整型
'q' signed long long int 8 -9,223,372,036,854,775,808 至 9,223,372,036,854,775,807
'Q' unsigned long long int 8 0 至 18,446,744,073,709,551,615
'f' float float 4 标准 IEEE 754 单精度浮点数
'd' double float 8 标准 IEEE 754 双精度浮点数(等同于 Python 默认 float)
Python标准库array模块用法:怎么创建固定类型数值数组
图 2:array 模块常用类型码决策与选型路线

三、创建固定类型数值数组的四种典型操作

在实际编码中,创建 array 对象的途径非常灵活,可以根据初始数据的来源(已有列表、原始二进制字节串、磁盘文件或动态追加)选择最便捷高效的方式。

1. 通过现有可迭代对象直接初始化

最直观的方式是将 Python 的 list、tuple 或 range 作为第二个参数传入构造函数:

from array import array

# 使用列表初始化 32 位有符号整型数组
numbers = [10, 20, 30, 40, 50]
int_arr = array('i', numbers)
print("整数数组:", int_arr)
# 示例输出: 整数数组: array('i', [10, 20, 30, 40, 50])

# 使用 range 直接构建连续序列
range_arr = array('l', range(1000))

# 创建双精度浮点数组
float_arr = array('d', [1.25, 3.14159, 2.71828])
print("元素大小(字节):", float_arr.itemsize)
# 示例输出: 元素大小(字节): 8

2. 从原始字节串(bytes)高速解析

在网络通信、底层驱动交互或传感器数据采集中,数据往往以未经包装的二进制 bytes 形式传输。使用 frombytes() 可以直接将连续字节流无拷贝或低开销地映射为数值数组:

from array import array

# 假设接收到底层 8 字节二进制流(包含两个 4 字节小端序 int)
raw_bytes = b'\x0a\x00\x00\x00\x14\x00\x00\x00'

# 创建空数组并从字节加载
arr = array('i')
arr.frombytes(raw_bytes)
print("从字节解析出的数值:", arr)
# 示例输出: 从字节解析出的数值: array('i', [10, 20])

# 同样支持随时导出回原始字节
exported_bytes = arr.tobytes()
print("反向导出字节串:", exported_bytes)
# 示例输出: 反向导出字节串: b'\n\x00\x00\x00\x14\x00\x00\x00'

3. 从磁盘二进制文件直接拉取(超快 I/O)

传统的文本格式(如 JSON、CSV)在读写百万级数值时涉及繁重的字符串编解码。array 提供了 tofile() 和 fromfile(),能直接以 C 数组二进制原样写入或读取文件:

from array import array
import os

# 创建包含 5 个双精度浮点数的数组并直接存入二进制文件
data = array('d', [10.5, 20.3, 30.1, 40.8, 50.2])
with open("sensor_cache.bin", "wb") as fp:
    data.tofile(fp)

# 从文件中精确读取恢复
restored = array('d')
with open("sensor_cache.bin", "rb") as fp:
    # 第二个参数指定读取的元素个数(非字节数)
    restored.fromfile(fp, 5)

print("从文件快速恢复的数组:", restored)
# 示例输出: 从文件快速恢复的数组: array('d', [10.5, 20.3, 30.1, 40.8, 50.2])

if os.path.exists("sensor_cache.bin"):
    os.remove("sensor_cache.bin")

4. 声明空数组后动态追加元素

如果不确定数据总量,可以只指定类型码创建空数组,随后在数据到达时调用 append() 单个追加,或使用 extend() 批量合并:

from array import array

# 声明一个空的无符号 8 位数组(0~255)
buffer = array('B')

# 单个追加
buffer.append(255)
buffer.append(128)

# 批量扩展
buffer.extend([10, 20, 30])
print("动态构建结果:", buffer)
# 示例输出: 动态构建结果: array('B', [255, 128, 10, 20, 30])
Python标准库array模块用法:怎么创建固定类型数值数组
图 3:创建固定类型数值数组的四种典型操作与适用场景

四、内存占用对比实测与关键避坑注意事项

1. 真实内存占用实测对比

我们可以使用 sys.getsizeof() 观察存储 1,000,000 个 32 位整型时,原生列表与 array 的真实内存差异:

import sys
from array import array

N = 1_000_000

# 1. 原生列表
raw_list = list(range(N))
# list 容器本身占用的指针内存 + 每个 PyLongObject 对象内存
list_container_bytes = sys.getsizeof(raw_list)
single_int_bytes = sys.getsizeof(10)
estimated_total_list_mb = (list_container_bytes + N * single_int_bytes) / (1024 * 1024)

# 2. array 模块(32位有符号整型 'i')
arr = array('i', range(N))
array_bytes = sys.getsizeof(arr)
array_mb = array_bytes / (1024 * 1024)

print(f"原生 list 总估计内存: {estimated_total_list_mb:.2f} MB")
print(f"array('i') 总内存:   {array_mb:.2f} MB")
# 示例输出:
# 原生 list 总估计内存: 34.33 MB
# array('i') 总内存:   3.82 MB

实测结果清晰表明,在同等规模数据下,array('i') 仅占约 3.82 MB 内存,相比原生列表直接降低了将近 90% 的内存消耗。

2. 常见错误与避坑准则

⚠️ 避坑提醒一:严格的类型与范围限制

array 不允许混入异构类型。如果你创建的是整型数组,传入字符串或浮点数会抛出 TypeError;如果传入数值超过该类型码的上下限,会立即抛出 OverflowError:

arr = array('b', [10, 20])
# 错误示范 1: 存入浮点数 -> TypeError: integer argument expected, got float
# arr.append(3.14)

# 错误示范 2: 存入超过 127 的数字 -> OverflowError: signed char is greater than maximum
# arr.append(200)
⚠️ 避坑提醒二:不支持多维嵌套矩阵

array 只支持一维线性存储。如果需要类似 [[1, 2], [3, 4]] 的二维表格或张量结构,array 无法直接表达多层嵌套。此时建议使用一维数组加步长索引计算(index = row * cols + col),或者直接选用 NumPy。

⚠️ 避坑提醒三:技术选型边界划分
  • 选择原生 list:日常轻量数据、元素数量少于数万、需要存放不同类型对象(如混存字典、字符串)。
  • 选择 array 模块:百万级同质数值、希望免除任何第三方库依赖、需要频繁将整块数据做二进制磁盘保存或网络字节流发送。
  • 选择 NumPy:需要多维矩阵、向量化广播运算、傅里叶变换或复杂的线性代数运算。

五、全文总结

Python 标准库的 array 模块通过底层 C 语言原生连续内存机制,在无需外部依赖的前提下,以极简的 API 为数值计算与数据传输提供了出色的内存压缩率和读写性能。掌握 typecode 的合理选型,结合从迭代器、字节串与二进制文件的灵活创建方法,即可在面对大量纯数值数据时写出更轻量、更高性能的 Python 代码。

Python标准库array模块用法:怎么创建固定类型数值数组
图 4:Python array 模块核心要点与适用边界全景总结
相关专题:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表