SelfDB:用 Python 元编程把数据库变成你的“私有记忆库”
> SelfDB 是一个将 SQLite 数据库封装为 Python 对象属性的库,让你像操作普通对象一样持久化数据,无需编写 SQL 或 ORM 映射。
## 一、它到底解决了什么痛点?
在 Python 开发中,持久化数据通常有几种选择:
1. **直接写 SQL** — 需要手动管理连接、游标、事务,代码冗长且容易出错。
2. **使用 ORM(如 SQLAlchemy、Django ORM)** — 需要定义模型类、字段映射、迁移脚本,对于小项目或原型开发显得过重。
3. **使用轻量级键值存储(如 shelve、pickle)** — 虽然简单,但缺乏类型安全、查询能力和并发控制。
SelfDB 的核心理念是:**如果你只需要将 Python 对象持久化到本地文件,为什么不能像访问普通属性一样直接读写?** 它通过 Python 的元编程(`__getattr__`、`__setattr__`)将对象的每个属性自动映射到 SQLite 表的一个列,每次赋值自动触发数据库写入,每次读取自动从数据库加载。
这意味着:
- 你不需要写任何 SQL 语句。
- 你不需要定义模型类或字段声明。
- 你不需要关心数据库连接的生命周期。
- 你不需要手动调用 `save()` 或 `commit()`。
## 二、快速上手:安装与第一个例子
### 安装
bash
pip install selfdb
### 基本用法
python
from selfdb import SelfDB
# 创建一个数据库连接(文件会自动创建)
db = SelfDB('mydata.db')
# 直接赋值,自动持久化
db.user_name = 'Alice'
db.user_age = 30
db.user_tags = ['admin', 'developer'] # 列表自动序列化
# 直接读取,自动从数据库加载
print(db.user_name) # 输出: Alice
print(db.user_age) # 输出: 30
print(db.user_tags) # 输出: ['admin', 'developer']
# 删除属性
del db.user_age
就这么简单。每次赋值,SelfDB 内部会执行 SQLite 的 `INSERT OR REPLACE`,每次读取会执行 `SELECT`。你甚至不需要手动关闭连接,SelfDB 在对象销毁或程序退出时会自动清理。
### 复杂数据类型的处理
SelfDB 支持 Python 的基本类型(int、float、str、bool),对于列表、字典、元组等复合类型,它会自动使用 JSON 序列化存储。这意味着你可以直接存储嵌套结构:
python
db.user_profile = {
'name': 'Bob',
'skills': ['Python', 'C++'],
'metadata': {'level': 2, 'active': True}
}
# 读取时自动反序列化为字典
profile = db.user_profile
print(profile['skills'][0]) # 输出: Python
### 事务与批量操作
虽然每个赋值都是独立写入,但 SelfDB 也提供了上下文管理器来支持原子性:
python
with db.transaction():
db.key1 = 'value1'
db.key2 = 'value2'
# 如果中途异常,所有操作回滚
## 三、核心亮点深入分析
### 1. 零配置、零样板代码
这是 SelfDB 最大的吸引力。相比 SQLAlchemy 需要定义 `Base`、`Column`、`session` 等概念,SelfDB 只需要一行 `SelfDB('file.db')` 就完成了所有初始化。特别适合:
- 快速原型验证
- 脚本工具的状态持久化
- 配置管理(替代 JSON/YAML 文件)
- 教学演示(让学生专注于逻辑而非数据库细节)
### 2. 基于 SQLite 的可靠性
虽然语法看起来像是一个玩具库,但底层是成熟的 SQLite 引擎。这意味着你获得:
- 事务支持(ACID)
- 并发安全(多个进程可以同时读取)
- 数据完整性(崩溃恢复)
- 零外部依赖(SQLite 是 Python 标准库的一部分)
### 3. 轻量且可移植
整个库只有几百行代码,没有依赖其他第三方包(除了标准库)。数据库文件就是单个 `.db` 文件,可以轻松备份、迁移或嵌入到其他系统中。
### 4. 动态属性映射的巧妙实现
SelfDB 通过重写 `__getattr__` 和 `__setattr__` 来拦截所有属性访问。当属性名以 `_` 开头时,它视为内部属性(如 `_connection`),否则视为用户数据。这种设计避免了与内部状态冲突,同时保持了 Python 的直观语法。
### 5. 支持类型提示与默认值
你可以通过类属性定义默认值,SelfDB 会将其合并到读取逻辑中:
python
class MyDB(SelfDB):
_defaults = {
'counter': 0,
'config': {'theme': 'dark'}
}
db = MyDB('app.db')
print(db.counter) # 输出 0(默认值)
db.counter += 1 # 修改后自动保存
## 四、适用场景与真实用例
### 场景 A:命令行工具的状态管理
写一个 CLI 工具,需要记住用户上次的选项、进度或缓存结果。用 SelfDB 只需要几行代码:
python
import click
from selfdb import SelfDB
state = SelfDB('cli_state.db')
@click.command()
@click.option('--verbose', is_flag=True)
def main(verbose):
if verbose:
state.verbose = True
else:
# 读取上次的设置
verbose = getattr(state, 'verbose', False)
click.echo(f'Verbose mode: {verbose}')
### 场景 B:Web 应用的轻量级会话存储
对于 Flask 或 FastAPI 的小型应用,不想引入 Redis 或数据库服务器时,SelfDB 可以作为会话存储的替代:
python
from flask import Flask, session
from selfdb import SelfDB
app = Flask(__name__)
db = SelfDB('sessions.db')
@app.route('/login')
def login():
db.session_user = 'alice'
return 'Logged in'
@app.route('/profile')
def profile():
user = db.session_user
return f'Hello {user}'
### 场景 C:机器学习实验的配置与结果记录
在 Jupyter Notebook 或训练脚本中,记录超参数和评估指标:
python
from selfdb import SelfDB
exp = SelfDB('experiment_1.db')
exp.learning_rate = 0.001
exp.batch_size = 64
exp.accuracy = 0.934
# 后续分析时直接读取
### 场景 D:嵌入式设备的配置持久化
在 Raspberry Pi 或 IoT 设备上,需要保存传感器校准值或设备ID,SelfDB 的轻量特性非常适合。
## 五、与其他同类项目的对比
### vs. SQLite 原生 + sqlite3 模块
- **SQLite 原生**:需要手动管理 SQL 语句、参数绑定、游标,代码量增加 5-10 倍。
- **SelfDB**:自动处理所有底层细节,但牺牲了复杂查询能力(如 JOIN、GROUP BY)。
### vs. SQLAlchemy(Core/ORM)
- **SQLAlchemy**:功能强大,支持多数据库、复杂关系、查询构建器,但学习曲线陡峭,配置繁琐。
- **SelfDB**:零学习成本,但仅支持键值对模式(属性名 → 值),不支持表关联、外键、索引优化。
### vs. TinyDB
- **TinyDB**:同样是纯 Python 的轻量级数据库,但需要定义 `Table` 和 `Document`,查询使用类 SQL 的语法。
- **SelfDB**:更极简,连表的概念都没有,整个数据库就是一个扁平的对象空间。TinyDB 支持更复杂的查询(`where` 条件),SelfDB 只支持属性名访问。
### vs. shelve(标准库)
- **shelve**:基于 dbm,只能存储 picklable 对象,不支持多线程写入,且没有 SQLite 的事务和崩溃恢复。
- **SelfDB**:基于 SQLite,更可靠,且数据文件可被其他 SQLite 工具(如 sqlite3 CLI)直接读取。
### vs. Redis(内存数据库)
- **Redis**:高性能网络服务,支持丰富的数据结构,但需要独立进程运行。
- **SelfDB**:本地文件,零部署,适合单机应用。
## 六、局限性与注意事项
1. **不支持复杂查询**:如果你需要 `SELECT * FROM table WHERE age > 30` 这种条件过滤,SelfDB 不适合。它只适合通过属性名精确读写。
2. **性能瓶颈**:每次属性赋值都会触发磁盘写入(SQLite 默认同步模式)。对于高频写入场景(如每秒数百次),性能可能成为问题。可以通过 `PRAGMA synchronous = OFF` 调整,但会牺牲可靠性。
3. **无索引支持**:因为数据模型是扁平的键值对,无法为特定字段建立索引。
4. **并发写入限制**:SQLite 支持多进程读,但写操作会锁定整个数据库。SelfDB 没有实现任何锁机制,因此多进程并发写可能产生 `database is locked` 错误。
5. **类型安全性**:由于是动态属性,IDE 无法提供自动补全或类型检查。
## 七、项目活跃度与社区
该项目由 fzakaria 开发,目前有 474 星,属于中等偏小众项目。Python 语言,代码仓库简洁,没有复杂的 CI/CD 或文档站。但核心代码质量不错,注释清晰,适合阅读源码学习元编程技巧。
## 八、总结与推荐
**SelfDB 适合谁?**
- 想快速持久化简单状态的 Python 开发者
- 不想引入重型 ORM 的小型项目
- 教学或演示场景(展示 Python 元编程之美)
- 边缘计算、嵌入式设备上的配置存储
**SelfDB 不适合谁?**
- 需要复杂查询、关系建模的应用
- 高并发写入场景
- 需要完整数据库管理(用户权限、备份恢复、迁移)的生产系统
如果你能接受它的局限性,SelfDB 会给你带来“写代码如行云流水”的体验。它把数据库的复杂度藏在了 Python 语言本身的语法糖之下,让你专注于业务逻辑而非数据存储细节。
> 项目链接:[https://github.com/fzakaria/selfdb](https://github.com/fzakaria/selfdb)