page contents

Python 3.15 shelve 这个新方法救了多少新手?

你有没有遇到过这种情况?辛辛苦苦写了个 Python 小工具,用 shelve 把数据存到本地数据库里,运行一个月之后突然发现:诶,我明明删了一大半数据,怎么磁盘文件还是几十 MB?

attachments-2026-07-f64GKVh16a4c595ce5c02.png你有没有遇到过这种情况?辛辛苦苦写了个 Python 小工具,用 shelve 把数据存到本地数据库里,运行一个月之后突然发现:诶,我明明删了一大半数据,怎么磁盘文件还是几十 MB?

别慌,这不是你的代码写错了。这是 shelve 和 dbm 这种老牌数据库一直以来都有的老毛病——删了数据不还磁盘空间。

好消息是,Python 3.15 给这两个模块都加了一个新方法,叫 reorganize()。这个东西就是来救场的。今天这篇就把这个痛点彻底讲清楚。

先看看 shelve 是干嘛的

很多人学 Python 的时候,听过 pickle(把对象序列化),听过 json(存字典),但 shelve 这个东西不一定知道。

shelve 的意思就是"搁架",你可以把它理解成 一个能存任何 Python 对象的字典。普通字典是程序关了就没了,shelve 是写进硬盘里,下次启动还能读出来。

用法特别简单,跟操作字典一样:

import shelve

 

# 打开一个 shelve 文件,存点数据进去

with shelve.open('users.db') as db:

    db['user_1'] = {'name': '小明', 'age': 18}

    db['user_2'] = {'name': '小红', 'age': 20}

    db['user_3'] = {'name': '小李', 'age': 22}

 

print("存好了")

是不是比 sqlite 简单多了?不用写 SQL,不用建表,dict 怎么用 shelve 就怎么用。所以很多新手写小工具(比如记事本、记账本、爬虫缓存)都喜欢用它。

dbm 是 shelve 的底层,区别是 dbm 只能存 bytes,shelve 能存任意 Python 对象。今天这两个模块的故事差不多,所以放一起讲。

那个让人崩溃的"文件越用越大"问题

假设你写了个记账小工具,每天往 shelve 里存一笔账。运行一年后你发现文件已经 200MB 了,这时候你决定清理一下,把三年前的旧数据都删了。

你兴冲冲地写了个脚本:

import shelve

import os

 

# 删掉所有 2024 年之前的旧账

with shelve.open('account.db', writeback=False) as db:

    for key in list(db.keys()):

        if key.startswith('2024_'):

            del db[key]

 

print(f"清理完了!文件大小: {os.path.getsize('account.db')} bytes")

代码跑完了,你满心欢喜地去看文件大小——还是 200MB。

你开始怀疑人生:明明数据删了啊?怎么磁盘空间不还回来?

其实这不是 bug,是 shelve/dbm 底层的 dbm 数据库的设计方式:当你删除一个条目时,dbm 只是把那块空间标记为"已删除",并不会真的把数据从文件里抹掉。这样做的目的是为了性能——频繁增删不用每次都重写整个文件。

但代价就是:你的文件会一直膨胀,即使有效数据已经很少了。

在 Python 3.15 之前,要解决这个问题你得手动绕一圈:把数据复制到一个新的 shelve 文件,删掉旧文件,把新文件重命名回去:

import shelve

import os

 

# 3.15 之前的老办法:手动重建文件

with shelve.open('account.db') as src:

    with shelve.open('account_new.db') as dst:

        for key in src:

            dst[key] = src[key]

 

os.remove('account.db')

os.rename('account_new.db', 'account.db')

看着就头大,对吧?光是这一段复制粘贴就够新手折腾半天的了。

Python 3.15 的解法:一行搞定

Python 3.15 给 shelve、dbm.dumb、dbm.sqlite3 这三个模块都加了同一个方法:reorganize()。

顾名思义,reorganize 就是把文件重新整理一遍,把那些被标记为"已删除"的空洞空间回收掉。用法简单到爆:

import shelve

import os

 

# 假设你已经删完数据了

size_before = os.path.getsize('account.db')

 

# Python 3.15 新写法:一行搞定

with shelve.open('account.db') as db:

    db.reorganize()

 

size_after = os.path.getsize('account.db')

print(f"整理前: {size_before} bytes")

print(f"整理后: {size_after} bytes")

就这样。没有临时文件,没有手动重命名,没有一堆繁琐的步骤。reorganize() 自己会处理好一切。

dbm 也是一样的用法:

import dbm

 

with dbm.open('cache.db', 'w') as db:

    db.reorganize()  # 同样一行搞定

实际效果对比:100MB 变 1MB

光说不练假把式。我写个完整的对比脚本,看看到底能省多少空间:

import shelve

import os

 

DB = 'test.db'

if os.path.exists(DB):

    os.remove(DB)

 

# 第一步:写入 10000 条数据

with shelve.open(DB) as db:

    for i in range(10000):

        db[f'key_{i}'] = f'这是第 {i} 条测试数据' * 10

print(f"刚写完: {os.path.getsize(DB)} bytes")

 

# 第二步:删掉 99% 的数据

with shelve.open(DB) as db:

    for i in range(9900):

        del db[f'key_{i}']

print(f"删完数据: {os.path.getsize(DB)} bytes  (还是这么大!)")

 

# 第三步:Python 3.15 新方法 reorganize

with shelve.open(DB) as db:

    db.reorganize()

print(f"reorganize 后: {os.path.getsize(DB)} bytes  (瘦下来了!)")

在我电脑上跑出来的结果大概是这样的:

刚写完 10000 条数据:1.2 MB

删掉 99% 之后:1.2 MB(看,文件根本没瘦!)

调用 reorganize() 之后:12 KB(足足小了 100 倍)

12 KB 跟 1.2 MB 的对比,就是"看似删了但其实没删"和"真的把空间还回去"的区别。

什么时候该调 reorganize?

这个方法不是让你写完每条数据都调一次的,那反而会拖慢程序。建议这么用:

1. 批量删除后立即调用

比如你写了个清理过期缓存的脚本,把 1000 条过期数据全删了,这时候调一次:

def clean_cache(db):

    for key in list(db.keys()):

        if is_expired(key):

            del db[key]

    db.reorganize()  # 删完顺手整理

2. 定期维护时调用

比如每周日凌晨跑个脚本:

import schedule

 

def weekly_cleanup():

    with shelve.open('account.db') as db:

        db.reorganize()

    print("每周整理完成")

 

schedule.every().sunday.at('03:00').do(weekly_cleanup)

3. 文件大到该瘦身时调用

如果你懒得定计划,那就写个判断:文件超过 50MB 就整理一次:

import shelve

import os

 

DB = 'cache.db'

MAX_SIZE = 50 * 1024 * 1024  # 50 MB

 

with shelve.open(DB) as db:

    # 正常读写...

    pass

 

# 用完后再判断要不要瘦身

if os.path.getsize(DB) > MAX_SIZE:

    with shelve.open(DB) as db:

        db.reorganize()

3 个容易踩的坑

坑 1:reorganize 过程会锁住文件

reorganize 整理的时候,数据库是不能被其他程序访问的。所以如果你有多个进程同时操作同一个 shelve 文件,记得错峰调用,别在用户活跃期去整理。

坑 2:数据量大时会有点慢

如果你数据库里有 1 亿条数据,reorganize 可能会跑几秒钟。所以建议放在后台或者维护窗口跑,别在主流程里同步调。

坑 3:reorganize 不一定 100% 还原到最小体积

dbm 内部还是会留一点点开销(页头、索引之类的),所以整理完的文件大小可能比"纯数据大小"稍微大一点点,这是正常的。不用追求绝对的最小值。

写在最后

Python 3.15 这次的 reorganize() 改动看着不起眼,但它解决的是一个被吐槽了十几年的老问题。以前你写脚本碰到"文件怎么越用越大",要么忍着,要么自己写那一段"建新文件-复制-删旧文件-重命名"的样板代码。

现在好了,一行 db.reorganize() 就能搞定。shelve 和 dbm 终于能像正经数据库那样干净利落地管理磁盘空间了。

如果你正好在用 shelve 写本地存储的小工具,又在纠结文件为啥越用越大,那就升级到 3.15 试试这个新方法吧。

 更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

  • 发表于 2026-07-07 09:42
  • 阅读 ( 28 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2251 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. Pack 2251 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章