Python sqlite3.adapt 函数
上次修改时间:2025 年 4 月 15 日
本综合指南探讨了 Python 的 sqlite3.adapt
函数,该函数可以为 SQLite 数据库启用自定义类型适配。我们将介绍基本用法、注册模式和实际示例。
基本定义
sqlite3.adapt
函数注册适配器可调用对象,以将 Python 对象转换为与 SQLite 兼容的类型。 它是 SQLite 类型系统的一部分。
主要特征:它能够在 SQLite 中存储自定义 Python 类型,适用于类和可调用对象,并与 DB-API 2.0 接口集成。
基本类型适配
这是一个简单的示例,展示了如何使用 sqlite3.adapt
适配自定义 Python 类型以存储在 SQLite 中。
import sqlite3 class Point: def __init__(self, x, y): self.x = x self.y = y def adapt_point(point): return f"{point.x};{point.y}".encode('utf-8') # Register the adapter sqlite3.register_adapter(Point, adapt_point) with sqlite3.connect(":memory:") as conn: conn.execute("CREATE TABLE points (p BLOB)") conn.execute("INSERT INTO points VALUES (?)", (Point(4, 5),)) row = conn.execute("SELECT p FROM points").fetchone() print(row[0]) # b'4;5'
此示例创建一个 Point 类并注册一个适配器函数,该函数将 Point 对象转换为字节。 存储 Points 时会自动调用适配器。
适配器必须返回 SQLite 原生支持的类型:None、int、float、str 或 bytes。 在这里,我们返回 bytes 用于 blob 存储。
适配复数
此示例演示了如何适配 Python 内置的复数以进行 SQLite 存储。
import sqlite3 def adapt_complex(c): return f"{c.real},{c.imag}".encode('utf-8') sqlite3.register_adapter(complex, adapt_complex) with sqlite3.connect(":memory:") as conn: conn.execute("CREATE TABLE complex_nums (num BLOB)") conn.execute("INSERT INTO complex_nums VALUES (?)", (3+4j,)) row = conn.execute("SELECT num FROM complex_nums").fetchone() print(row[0]) # b'3.0,4.0'
在这里,我们将 Python 的复数适配为字节字符串格式。 适配器将实部和虚部都转换为字符串表示形式。
这种方法允许在 SQLite 中存储复数,但查询它们需要在检索时进行额外的转换逻辑。
适配十进制数
此示例显示了如何正确适配 Decimal 数字以进行精确存储。
import sqlite3 from decimal import Decimal def adapt_decimal(d): return str(d) sqlite3.register_adapter(Decimal, adapt_decimal) with sqlite3.connect(":memory:") as conn: conn.execute("CREATE TABLE prices (amount TEXT)") price = Decimal('19.99') conn.execute("INSERT INTO prices VALUES (?)", (price,)) row = conn.execute("SELECT amount FROM prices").fetchone() print(Decimal(row[0])) # 19.99
Decimal 数字需要小心处理以保持精度。 此适配器将它们转换为字符串,以便在 SQLite TEXT 列中进行精确存储。
检索时,您需要手动转换回 Decimal,如 print 语句所示。 这保留了完整的十进制精度。
适配具有属性的自定义对象
此示例将具有多个属性的自定义对象适配为 JSON 字符串。
import sqlite3 import json class Product: def __init__(self, id, name, price): self.id = id self.name = name self.price = price def adapt_product(product): return json.dumps({ 'id': product.id, 'name': product.name, 'price': product.price }) sqlite3.register_adapter(Product, adapt_product) with sqlite3.connect(":memory:") as conn: conn.execute("CREATE TABLE products (data TEXT)") p = Product(101, "Widget", 9.99) conn.execute("INSERT INTO products VALUES (?)", (p,)) row = conn.execute("SELECT data FROM products").fetchone() print(row[0]) # JSON string
这种方法将复杂对象序列化为 JSON 字符串,以实现灵活存储。 JSON 格式保留了对象结构,并允许使用 SQLite 的 JSON 函数查询特定字段。
请注意,SQLite 必须编译 JSON 支持,才能对存储的 JSON 数据进行高级查询。
适配 Datetime 对象
此示例将 Python datetime 对象适配为 ISO 格式字符串。
import sqlite3 from datetime import datetime def adapt_datetime(dt): return dt.isoformat() sqlite3.register_adapter(datetime, adapt_datetime) with sqlite3.connect(":memory:") as conn: conn.execute("CREATE TABLE events (time TEXT)") now = datetime.now() conn.execute("INSERT INTO events VALUES (?)", (now,)) row = conn.execute("SELECT time FROM events").fetchone() print(row[0]) # ISO formatted datetime string
ISO 格式非常适合 datetime 存储,因为它既易于阅读又可排序。 此适配器将 datetime 对象转换为此标准格式的字符串。
对于有时区意识的日期时间,您可能希望在适配器中包含 UTC 偏移信息,以便完全保留时区。
使用类方法进行适配
此示例演示了如何使用类方法作为适配器,以实现更简洁的代码组织。
import sqlite3 class Color: def __init__(self, r, g, b): self.r = r self.g = g self.b = b @classmethod def adapt(cls, color): return f"{color.r},{color.g},{color.b}" sqlite3.register_adapter(Color, Color.adapt) with sqlite3.connect(":memory:") as conn: conn.execute("CREATE TABLE colors (value TEXT)") red = Color(255, 0, 0) conn.execute("INSERT INTO colors VALUES (?)", (red,)) row = conn.execute("SELECT value FROM colors").fetchone() print(row[0]) # '255,0,0'
使用类方法作为适配器使转换逻辑与它适配的类紧密结合。 这提高了代码组织和可维护性。
类方法可以访问所有类属性和方法,从而在需要时允许更复杂的适配逻辑。
结合适配器和转换器
这个完整的例子展示了如何适配 Python 对象进行存储,以及如何将 SQLite 值转换回 Python 对象。
import sqlite3 class Measurement: def __init__(self, value, unit): self.value = value self.unit = unit def __repr__(self): return f"Measurement({self.value}, '{self.unit}')" def adapt_measurement(m): return f"{m.value}|{m.unit}".encode('utf-8') def convert_measurement(s): value, unit = s.decode('utf-8').split('|') return Measurement(float(value), unit) sqlite3.register_adapter(Measurement, adapt_measurement) sqlite3.register_converter("MEASUREMENT", convert_measurement) with sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES) as conn: conn.execute("CREATE TABLE data (m MEASUREMENT)") m = Measurement(42.5, "cm") conn.execute("INSERT INTO data VALUES (?)", (m,)) row = conn.execute("SELECT m FROM data").fetchone() print(row[0]) # Measurement(42.5, 'cm')
这个完整的解决方案展示了双向转换。 适配器处理存储,而转换器在检索数据时重建对象。
请注意 detect_types=sqlite3.PARSE_DECLTYPES
参数,该参数根据列类型声明启用转换器功能。
最佳实践
- 选择适当的格式: 使用保留所有必要数据的格式
- 考虑查询需求: 适配支持您的查询模式的格式
- 记录适配: 清楚地记录任何自定义类型适配
- 彻底测试: 验证往返转换是否正常工作
- 处理边缘情况: 确保适配器适用于 null/边缘值
资料来源
作者
列出所有 Python 教程。