如何保存以整数三元组为键的大型Python字典?
可选方案推荐
针对你的只读查找场景(50万条整数三元组键+500长度整数列表值),除了pickle之外,推荐以下几种高效方案:
1. 自定义二进制格式
完全手动控制序列化/反序列化逻辑,无额外格式开销,读取速度最快,适合对性能有极致要求的场景。
实现思路:
- 按固定字节顺序,将每个三元组键的3个整数、值列表的500个整数写入文件
- 读取时直接按块读取二进制数据,还原后构建查找字典
代码示例:
import struct # 写入数据 def save_dict_to_bin(my_dict, filename): # 每条数据大小:(3个键整数 + 500个值整数) * 4字节/整数 = 2012字节 with open(filename, 'wb') as f: # 先写入总条数 f.write(struct.pack('<I', len(my_dict))) for key, value in my_dict.items(): # 写入三元组键 f.write(struct.pack('<iii', *key)) # 写入值列表 f.write(struct.pack('<' + 'i'*500, *value)) # 读取数据 def load_dict_from_bin(filename): my_dict = {} with open(filename, 'rb') as f: count = struct.unpack('<I', f.read(4))[0] for _ in range(count): key = struct.unpack('<iii', f.read(12)) value = list(struct.unpack('<' + 'i'*500, f.read(2000))) my_dict[key] = value return my_dict # 使用示例 save_dict_to_bin(my_dict, 'lookup_table.bin') loaded_dict = load_dict_from_bin('lookup_table.bin')
2. NumPy 结构化数组 + 哈希映射
利用NumPy的高效数组存储特性,将键和值分别存入数组,读取后快速构建查找字典,适合纯数值型数据场景。
实现思路:
- 将三元组键转换为结构化数组,值存入二维数组
- 读取后通过
zip快速拼接成字典
代码示例:
import numpy as np # 写入数据 def save_dict_to_npy(my_dict, filename_prefix): keys = np.array(list(my_dict.keys()), dtype=[('a', int), ('b', int), ('c', int)]) values = np.array(list(my_dict.values()), dtype=int) np.save(f'{filename_prefix}_keys.npy', keys) np.save(f'{filename_prefix}_values.npy', values) # 读取数据 def load_dict_from_npy(filename_prefix): keys = np.load(f'{filename_prefix}_keys.npy') values = np.load(f'{filename_prefix}_values.npy') return {tuple(k): v.tolist() for k, v in zip(keys, values)} # 使用示例 save_dict_to_npy(my_dict, 'lookup_table') loaded_dict = load_dict_from_npy('lookup_table')
3. SQLite 数据库
适合需要灵活查询、不想自己处理序列化逻辑的场景,通过复合索引保证查询速度,支持单条或批量查询。
实现思路:
- 创建包含三个键列(a,b,c)和一个值列(存储二进制序列化的列表)的表
- 写入时批量插入数据,创建
(a,b,c)复合唯一索引 - 读取时通过SQL查询直接获取对应值
代码示例:
import sqlite3 import pickle # 仅用于序列化单个值列表,非整个字典 def init_db(db_path): conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS lookup_table ( a INTEGER, b INTEGER, c INTEGER, value BLOB, PRIMARY KEY (a, b, c) ) ''') conn.commit() conn.close() def save_dict_to_sqlite(my_dict, db_path): conn = sqlite3.connect(db_path) cursor = conn.cursor() data = [(k[0], k[1], k[2], pickle.dumps(v)) for k, v in my_dict.items()] cursor.executemany('INSERT INTO lookup_table VALUES (?, ?, ?, ?)', data) conn.commit() conn.close() def lookup_from_sqlite(db_path, key): conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute('SELECT value FROM lookup_table WHERE a=? AND b=? AND c=?', key) result = cursor.fetchone() conn.close() return pickle.loads(result[0]) if result else None # 使用示例 init_db('lookup.db') save_dict_to_sqlite(my_dict, 'lookup.db') # 查询示例 print(lookup_from_sqlite('lookup.db', (1,2,3)))
4. Apache Parquet 文件
列式存储格式,支持压缩,适合大数据量高效存储与跨语言读取的场景。
实现思路:
- 将三元组键拆分为三个单独列,值列表拆分为500个列(或存储为数组类型)
- 使用PyArrow写入Parquet文件,读取后转换为字典
代码示例(使用PyArrow):
import pyarrow as pa import pyarrow.parquet as pq def save_dict_to_parquet(my_dict, filename): data = { 'a': [], 'b': [], 'c': [], **{f'val_{i}': [] for i in range(500)} } for (a,b,c), vals in my_dict.items(): data['a'].append(a) data['b'].append(b) data['c'].append(c) for i in range(500): data[f'val_{i}'].append(vals[i]) table = pa.Table.from_pydict(data) pq.write_table(table, filename) def load_dict_from_parquet(filename): table = pq.read_table(filename) df = table.to_pandas() my_dict = {} for _, row in df.iterrows(): key = (row['a'], row['b'], row['c']) value = [row[f'val_{i}'] for i in range(500)] my_dict[key] = value return my_dict # 使用示例 save_dict_to_parquet(my_dict, 'lookup_table.parquet') loaded_dict = load_dict_from_parquet('lookup_table.parquet')
方案选择建议
- 追求极致读取速度:选自定义二进制格式或NumPy方案
- 需要灵活查询/多语言支持:选SQLite或Parquet
- 希望代码简洁且性能均衡:选NumPy方案
内容的提问来源于stack exchange,提问作者Simd
相关产品推荐
相关产品推荐

