sqlite3.Row 是 SQLite 的行工厂,支持按列名(如 row["name"])访问字段,但本身不是字典,需显式调用 dict(row) 转换;它只读、不可修改,不支持 .keys() 直接调用,且 fetchall() 后须用列表推导式 [dict(row) for row in rows] 才能得到字典列表。

sqlite3.Row 是什么,为什么它不直接等于字典
sqlite3.Row 是 SQLite 连接对象的一个行工厂(row factory),它让查询结果支持用列名(如 row["name"])访问字段,但 row 本身不是 dict —— 它是只读的、类元组对象,不支持 .keys()、.items() 或修改操作。很多人误以为设了 row_factory = sqlite3.Row 就能当字典用,结果调 dict(row) 报错或得到空字典,就是因为没理解它的底层行为。
真正能转成字典的,是靠 row 实例的映射协议(mapping protocol):它实现了 __getitem__ 和 keys() 等方法,所以可以被 dict() 构造函数识别并转换。
- 必须显式调用
dict(row)才能得到可变字典 -
row.keys()返回的是列名列表,不是字典键视图 - 不能对
row直接赋值,比如row["age"] = 25会抛TypeError
怎么正确启用并使用 sqlite3.Row
启用 sqlite3.Row 只需在创建连接后设置 row_factory 属性,之后所有 cursor.execute() 返回的 row 都具备列名访问能力。
import sqlite3
conn = sqlite3.connect(":memory:")
conn.row_factory = sqlite3.Row # 关键一步
<p>cur = conn.cursor()
cur.execute("CREATE TABLE users (id INTEGER, name TEXT, age INTEGER)")
cur.execute("INSERT INTO users VALUES (1, 'Alice', 30)")
cur.execute("SELECT * FROM users")
row = cur.fetchone() # row 是 sqlite3.Row 实例
print(row["name"]) # ✅ 正确:输出 'Alice'
print(row[1]) # ✅ 也支持索引访问
print(dict(row)) # ✅ 转为字典:{'id': 1, 'name': 'Alice', 'age': 30}</p>
- 必须在
conn.cursor()之前设置row_factory,之后设置无效 - 如果用了
with sqlite3.connect(...) as conn:,也要在with块内第一时间设row_factory - 不建议全局修改
sqlite3.row_factory(那是模块级配置,影响所有后续连接)
常见踩坑:fetchall() 后想批量转字典却忘了遍历
cursor.fetchall() 返回的是 list,每个元素是 sqlite3.Row,不是整个结果集自动变成字典列表。直接 dict(rows) 会报错,因为 dict() 期望接收键值对序列,而不是 Row 列表。
- ❌ 错误:
dict(cur.fetchall())→TypeError: cannot convert dictionary update sequence element #0 to a sequence - ✅ 正确:用列表推导式逐个转:
[dict(row) for row in cur.fetchall()] - ⚠️ 注意性能:如果结果集很大,
[dict(row) for row in ...]会一次性加载全部到内存;需要流式处理时,应改用for row in cur:+dict(row)
替代方案:什么时候不该用 sqlite3.Row
如果你只需要纯字典且不关心性能,sqlite3.Row 没问题;但若项目已用 pydantic 或 dataclass 做数据建模,或者需要默认值、类型转换、嵌套结构,硬套 dict(row) 就容易漏逻辑。
- 需要字段默认值?
sqlite3.Row不提供,得自己写{k: row[k] or default_value for k in row.keys()} - 想把
NULL自动转成None?它本来就会,但整数字段存了"123"字符串?不会自动转类型 ——sqlite3.Row不做类型推断 - 高并发写入场景下,频繁
dict(row)会产生额外对象开销,不如用namedtuple或自定义工厂函数复用结构
最常被忽略的一点:sqlite3.Row 的列名大小写敏感,且完全取决于 SQL 中的 AS 别名或原始字段名;如果 SELECT 里写了 SELECT NAME AS name,那 row["NAME"] 就取不到 —— 这种细节在线上查不出数据时特别难定位。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











