Python sqlite3.Connection.row_factory
上次修改时间:2025 年 4 月 15 日
本综合指南探讨了 Python 的 sqlite3.Connection.row_factory
属性,该属性控制从 SQLite 查询返回行的方式。我们将涵盖基本用法、内置工厂、自定义工厂和实际示例。
基本定义
SQLite 连接的 row_factory
属性决定了结果行如何从游标操作中返回。默认情况下,行作为元组返回。
主要特点:它接受一个处理每一行的可调用对象,可以随时更改,并影响从连接创建的所有游标。该工厂接收游标和行元组作为参数。
默认行工厂(元组)
默认情况下,行作为元组返回。此示例演示了默认行为,而无需设置任何行工厂。
import sqlite3 with sqlite3.connect(':memory:') as conn: conn.execute("CREATE TABLE test (id INTEGER, name TEXT)") conn.execute("INSERT INTO test VALUES (1, 'Alice')") cursor = conn.cursor() cursor.execute("SELECT * FROM test") row = cursor.fetchone() print(type(row)) # <class 'tuple'> print(row[0], row[1]) # Access by index
此示例显示了默认行为,其中行作为元组返回。通过数字索引(第一列为 0)访问列值。
元组格式简单且内存效率高,但可读性不如命名访问,尤其是在列很多或架构更改时。
使用 sqlite3.Row 工厂
sqlite3.Row
工厂提供对列的命名访问和其他有用的功能。这是推荐的内置工厂。
import sqlite3 with sqlite3.connect(':memory:') as conn: conn.row_factory = sqlite3.Row conn.execute("CREATE TABLE users (id INTEGER, name TEXT, age INTEGER)") conn.execute("INSERT INTO users VALUES (1, 'Bob', 35)") cursor = conn.cursor() cursor.execute("SELECT * FROM users") row = cursor.fetchone() print(row['name']) # Access by name print(row.keys()) # Column names print(row[1]) # Still accessible by index
此示例演示了使用 sqlite3.Row
,它比元组提供了几个优点。可以通过名称或索引访问列。
其他功能包括:keys
方法获取列名、支持相等比较以及显示值的字符串表示形式。
自定义字典工厂
您可以创建一个自定义工厂,将行作为字典返回,以获得更大的灵活性。此示例显示了一个简单的字典工厂。
import sqlite3 def dict_factory(cursor, row): return {col[0]: row[idx] for idx, col in enumerate(cursor.description)} with sqlite3.connect(':memory:') as conn: conn.row_factory = dict_factory conn.execute("CREATE TABLE products (id INTEGER, name TEXT, price REAL)") conn.execute("INSERT INTO products VALUES (1, 'Laptop', 999.99)") cursor = conn.cursor() cursor.execute("SELECT * FROM products") row = cursor.fetchone() print(row['name'], row['price']) # Access by name print(row) # Full dictionary
此自定义工厂将每一行转换为一个字典,其中键是列名,值是行值。cursor.description
提供列元数据。
字典访问非常易读且灵活,但比元组占用更多的内存。在处理大量列时尤其有用。
命名元组工厂
为了在可读性和性能之间取得平衡,您可以使用命名元组。此示例使用 collections.namedtuple
创建一个工厂。
import sqlite3 from collections import namedtuple def namedtuple_factory(cursor, row): fields = [col[0] for col in cursor.description] Row = namedtuple('Row', fields) return Row(*row) with sqlite3.connect(':memory:') as conn: conn.row_factory = namedtuple_factory conn.execute("CREATE TABLE employees (id INTEGER, name TEXT, department TEXT)") conn.execute("INSERT INTO employees VALUES (1, 'Carol', 'HR')") cursor = conn.cursor() cursor.execute("SELECT * FROM employees") row = cursor.fetchone() print(row.name, row.department) # Access by attribute print(row[0], row[1]) # Still accessible by index
此工厂为每个查询结果动态创建一个命名元组类。命名元组提供属性样式的访问,同时保持元组的性能。
优点是代码干净、可读,与字典相比,内存开销最小。命名元组类为每个查询创建一次。
类型转换工厂
行工厂还可以执行类型转换。此示例自动将特定列转换为 Python 类型。
import sqlite3 from datetime import datetime def convert_types(row): converted = [] for value in row: if isinstance(value, str) and value.startswith('date:'): converted.append(datetime.strptime(value[5:], '%Y-%m-%d').date()) else: converted.append(value) return converted def type_aware_factory(cursor, row): row = convert_types(row) return {col[0]: row[idx] for idx, col in enumerate(cursor.description)} with sqlite3.connect(':memory:') as conn: conn.row_factory = type_aware_factory conn.execute("CREATE TABLE events (id INTEGER, name TEXT, event_date TEXT)") conn.execute("INSERT INTO events VALUES (1, 'Meeting', 'date:2025-04-15')") cursor = conn.cursor() cursor.execute("SELECT * FROM events") row = cursor.fetchone() print(type(row['event_date'])) # <class 'datetime.date'> print(row['event_date'].year) # 2025
这个高级工厂检测特殊的字符串模式(如“date:YYYY-MM-DD”),并自动将它们转换为 Python 日期对象。
当您需要在应用程序中保持一致的类型处理或使用自定义 SQLite 类型适配器时,此类工厂非常有用。
不区分大小写的字典工厂
对于不区分大小写的列访问,您可以修改字典工厂。此示例显示了一个将列名规范化为小写的工厂。
import sqlite3 def case_insensitive_factory(cursor, row): return {col[0].lower(): row[idx] for idx, col in enumerate(cursor.description)} with sqlite3.connect(':memory:') as conn: conn.row_factory = case_insensitive_factory conn.execute("CREATE TABLE books (ID INTEGER, Title TEXT, Author TEXT)") conn.execute("INSERT INTO books VALUES (1, 'Python Guide', 'J. Smith')") cursor = conn.cursor() cursor.execute("SELECT * FROM books") row = cursor.fetchone() # All these work regardless of original case print(row['id'], row['title'], row['author']) print(row['ID'], row['Title'], row['Author']) print(row['Id'], row['tiTle'], row['AUTHOR'])
此工厂将返回的字典中的所有列名规范化为小写,从而使列访问不区分大小写,同时保留原始值。
当您处理具有不一致列命名约定的数据库或想要标准化访问模式时,这尤其有用。
自定义类工厂
为了获得最大的控制权,您可以返回自定义类实例。此示例创建了一个工厂,该工厂返回具有属性和字典访问权限的对象。
import sqlite3 class Record: def __init__(self, data): self._data = data def __getattr__(self, name): try: return self._data[name] except KeyError: raise AttributeError(f"No such attribute: {name}") def __getitem__(self, key): return self._data[key] def __repr__(self): return f"Record({self._data})" def class_factory(cursor, row): data = {col[0]: row[idx] for idx, col in enumerate(cursor.description)} return Record(data) with sqlite3.connect(':memory:') as conn: conn.row_factory = class_factory conn.execute("CREATE TABLE inventory (item_id INTEGER, item_name TEXT, quantity INTEGER)") conn.execute("INSERT INTO inventory VALUES (101, 'Keyboard', 15)") cursor = conn.cursor() cursor.execute("SELECT * FROM inventory") row = cursor.fetchone() print(row.item_name) # Attribute access print(row['quantity']) # Dictionary access print(row) # Custom representation
此工厂返回自定义 Record
类的实例,该类支持对列值进行属性样式和字典样式访问。
自定义类工厂提供了最大的灵活性,允许您向结果对象添加方法、属性和其他行为。
最佳实践
- 首选 sqlite3.Row:在大多数情况下,它提供了最佳平衡
- 考虑内存使用情况:命名元组比字典使用更少的内存
- 记录您的工厂:尤其是在使用自定义工厂时
- 尽早设置工厂:在执行需要它的查询之前
- 重用工厂:创建一次并在连接之间重用
资料来源
作者
列出所有 Python 教程。