Python sqlite3.register_converter 函数
上次修改时间:2025 年 4 月 15 日
本综合指南探讨了 Python 的 sqlite3.register_converter 函数,该函数允许在从 SQLite 检索数据时进行自定义类型转换。
基本定义
sqlite3.register_converter 函数注册一个可调用对象,用于将特定类型的 SQLite 值转换为 Python 对象。它与 sqlite3.connect 中的 detect_types 参数一起使用。
主要特征:它将 SQLite 类型映射到 Python 对象,支持复杂的类型处理,并与内置类型和自定义类型一起使用。转换发生在从数据库获取数据时。
基本类型转换
此示例显示如何为 SQLite DATE 类型注册一个转换器,将其转换为 Python date 对象。
import sqlite3
from datetime import date
def convert_date(val):
return date.fromisoformat(val.decode())
with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
sqlite3.register_converter("DATE", convert_date)
conn.execute("CREATE TABLE events(id INTEGER, event_date DATE)")
conn.execute("INSERT INTO events VALUES (1, '2025-04-15')")
row = conn.execute("SELECT * FROM events").fetchone()
print(row[1], type(row[1])) # 2025-04-15 <class 'datetime.date'>
转换器函数接受来自 SQLite 的字节对象并返回一个 Python date。 PARSE_DECLTYPES 标志启用从列声明中进行类型检测。
此模式对于在提取操作期间自动将数据库原生格式转换为 Python 对象非常有用。
转换 JSON 数据
此示例演示了如何自动将存储在 SQLite 中的 JSON 字符串转换为 Python 字典。
import sqlite3
import json
def convert_json(val):
return json.loads(val.decode())
with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
sqlite3.register_converter("JSON", convert_json)
conn.execute("CREATE TABLE configs(id INTEGER, settings JSON)")
conn.execute("INSERT INTO configs VALUES (1, '{\"theme\": \"dark\", \"notifications\": true}')")
row = conn.execute("SELECT * FROM configs").fetchone()
print(row[1]['theme']) # dark
转换器在检索数据时自动反序列化 JSON 字符串。 这对于在 SQLite 中存储半结构化数据特别有用。
请注意,SQLite 没有原生的 JSON 类型 - 我们使用类型名称作为转换器识别的约定。
自定义对象转换
此示例显示如何将 SQLite 值转换为自定义 Python 对象。
import sqlite3
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
def convert_point(val):
x, y = map(float, val.decode().split(','))
return Point(x, y)
with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
sqlite3.register_converter("POINT", convert_point)
conn.execute("CREATE TABLE shapes(id INTEGER, location POINT)")
conn.execute("INSERT INTO shapes VALUES (1, '3.5,4.2')")
row = conn.execute("SELECT * FROM shapes").fetchone()
print(row[1]) # Point(3.5, 4.2)
在这里,我们将点作为逗号分隔的字符串存储在 SQLite 中,并在检索时将其转换为 Point 对象。 转换器处理解析逻辑。
此模式对于需要在关系数据库中持久化的特定于域的对象很有用。
二进制数据转换
此示例演示了将二进制数据转换为自定义 Python 对象。
import sqlite3
import pickle
class CustomData:
def __init__(self, data):
self.data = data
def __repr__(self):
return f"CustomData({self.data!r})"
def convert_custom(val):
return pickle.loads(val)
with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
sqlite3.register_converter("CUSTOM", convert_custom)
original = CustomData([1, 2, 3])
conn.execute("CREATE TABLE data_store(id INTEGER, data CUSTOM)")
conn.execute("INSERT INTO data_store VALUES (1, ?)",
(pickle.dumps(original),))
row = conn.execute("SELECT * FROM data_store").fetchone()
print(row[1]) # CustomData([1, 2, 3])
此示例使用 pickle 将 Python 对象序列化为二进制格式,以便存储在 SQLite 中。 转换器在检索时取消 pickle 数据。
虽然功能强大,但在加载不受信任的数据时,请谨慎使用 pickle,因为它存在安全隐患。
多个转换器
此示例显示了为不同的 SQLite 类型注册多个转换器。
import sqlite3
from datetime import datetime, date
def convert_datetime(val):
return datetime.strptime(val.decode(), "%Y-%m-%d %H:%M:%S")
def convert_date(val):
return date.fromisoformat(val.decode())
with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
sqlite3.register_converter("DATETIME", convert_datetime)
sqlite3.register_converter("DATE", convert_date)
conn.execute("""CREATE TABLE events(
id INTEGER,
event_date DATE,
created_at DATETIME)""")
conn.execute("""INSERT INTO events VALUES
(1, '2025-04-15', '2025-04-15 14:30:00')""")
row = conn.execute("SELECT * FROM events").fetchone()
print(f"Date: {row[1]}, Datetime: {row[2]}")
在这里,我们为 DATE 和 DATETIME 类型注册单独的转换器。 每个转换器都处理其特定格式并返回适当的 Python 对象。
这种方法可以对如何将不同的 SQLite 类型转换为 Python 对象进行细粒度的控制。
转换器中的错误处理
此示例演示了转换器函数中的正确错误处理。
import sqlite3
from datetime import date
def safe_date_converter(val):
try:
return date.fromisoformat(val.decode())
except (ValueError, AttributeError) as e:
print(f"Conversion error: {e}")
return None
with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
sqlite3.register_converter("DATE", safe_date_converter)
conn.execute("CREATE TABLE events(id INTEGER, event_date DATE)")
conn.execute("INSERT INTO events VALUES (1, 'invalid-date')")
row = conn.execute("SELECT * FROM events").fetchone()
print(row[1]) # None (due to conversion error)
转换器包括错误处理以管理格式错误的数据。 当转换失败时,它返回 None,从而防止应用程序崩溃。
强大的转换器应始终处理输入数据中的潜在错误,以保持应用程序的稳定性。
与适配器结合
此示例显示了将转换器与适配器一起使用以进行双向转换。
import sqlite3
from decimal import Decimal
def adapt_decimal(d):
return str(d)
def convert_decimal(val):
return Decimal(val.decode())
# Register the adapter and converter
sqlite3.register_adapter(Decimal, adapt_decimal)
sqlite3.register_converter("DECIMAL", convert_decimal)
with sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
conn.execute("CREATE TABLE prices(id INTEGER, amount DECIMAL)")
price = Decimal('19.99')
conn.execute("INSERT INTO prices VALUES (1, ?)", (price,))
row = conn.execute("SELECT * FROM prices").fetchone()
print(row[1], type(row[1])) # 19.99 <class 'decimal.Decimal'>
适配器在存储数据时将 Python 对象转换为与 SQLite 兼容的格式,而转换器在检索时将其转换回来。 这提供了无缝的双向转换。
该组合功能强大,可以处理自定义类型,同时保持数据库兼容性。
最佳实践
- 处理错误: 转换器应优雅地管理格式错误的数据
- 使用高效格式: 选择紧凑的存储表示形式
- 文档类型: 清楚地记录自定义类型约定
- 考虑性能: 复杂的转换可能会影响速度
- 彻底测试: 验证类型转换中的边缘情况
资料来源
作者
列出所有 Python 教程。