ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

python的sqlite的Row对象:TaoToken统一Key下从sqlite3.Row到字典的转换实践

2026/10/4 9:57:31 拓冰建站 浏览量
python的sqlite的Row对象:TaoToken统一Key下从sqlite3.Row到字典的转换实践 1. 为什么 sqlite3.Row 总让人又爱又恨从取值到转字典的真实场景刚接触 Python 标准库 sqlite3 的时候很多人会默认以为查询结果就是字典结果row[symbol]一写就报TypeError: tuple indices must be integers or slices, not str。这个报错几乎每个用 sqlite3 做本地数据读取的人都踩过。原因很简单sqlite3 默认返回的是元组tuple不是字典也不是 sqlite3.Row。你只有显式设置了conn.row_factory sqlite3.Row查询结果才会变成支持按列名访问的 Row 对象。那 sqlite3.Row 到底是什么它是 Python 标准库里一个轻量的行对象行为介于元组和字典之间。它支持整数下标row[0]也支持列名下标row[symbol]还能调用row.keys()拿到列名列表tuple(row)转成普通元组。它不能直接json.dumps因为它不是 dict也不是 list直接序列化会抛TypeError: Object of type Row is not JSON serializable。这就是为什么「Row 转 dict」成了高频需求。适合谁看如果你在做本地小工具、爬虫落库、数据分析预处理、桌面应用缓存或者用 Python 脚本读取 SQLite 文件后要输出 JSON 给前端或其他服务这篇就是给你写的。场景很具体查询结果默认返回 Row你需要按列名访问、转 dict、转 JSON还要保证类型正确。我试过在一个数据导出脚本里直接json.dumps(rows)结果整个脚本崩在序列化那一步排查了十几分钟才反应过来 Row 不是 dict。后来统一封装了一个row_to_dict函数问题才彻底消失。这篇会把连接配置、row_factory 设置、字段映射代码、验证查询输出、类型检查步骤全部给出来并且放在 TaoToken 统一 Key/API 通道下完成本地数据读取与结构化输出。也就是说本地 SQLite 负责存储TaoToken 负责把结构化后的数据接到模型侧做进一步处理两边职责清晰。核心检索词先明确python sqlite3 Row 对象转字典、row_factory 设置、sqlite3.Row 按列名取值、Row 转 JSON 序列化。这几个词贯穿全文你照着做就能跑通。2. TaoToken 统一 Key 前置准备让本地数据读取接上模型通道在写 SQLite 代码之前先把 TaoToken 这一侧的准备工作做完。为什么本地读 SQLite 还要配 TaoToken因为很多人的真实流程是SQLite 里存了业务数据读出来转成 dict/JSON 之后要送给模型做摘要、分类、字段抽取或者生成报告。如果每次调用不同模型都要换一套 Key 和 Base URL脚本会变得很难维护。TaoToken 提供统一 Key 和统一 API 通道你只需要维护一份配置模型切换时改 Model ID 就行。先拿 Key。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 进入控制台在 API Keys 页面创建一个新 Key。建议按用途命名比如sqlite-export-agent方便后面排查是哪个脚本在用。创建后立刻复制保存页面刷新后通常不再完整显示。拿到 Key 之后你需要记住三个核心要素后面所有配置都围绕它们展开要素值说明Base URLhttps://taotoken.net/api统一 API 入口不加 UTMAPI Key控制台创建的那串放在环境变量里别硬编码Model ID按需选择例如对话类、编码类模型这里要强调一个坑Base URL 是https://taotoken.net/api不要自己在后面乱加/v1或者/chat/completions之外的路径具体拼接方式以接入文档为准。文档地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有各语言 SDK 的完整示例。环境变量配置建议这样写Linux/macOS 用 exportWindows 用 set 或者系统环境变量面板export TAOTOKEN_API_KEY你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你用的是 Claude Code 这类编码工具或者 Cline、Codex 这类带 MCP 的客户端配置项要写全三件套Base URL、Key、Model ID。少任何一个都会出现 401 或者 model not found。Claude Code 的接入可以参考 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有 settings 配置片段。长期做编码和 Agent 任务的话Coding Plan 更划算地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。它适合那种每天都要跑脚本、频繁调用模型的场景比按次计费更稳定。前置准备做完你的本地脚本就有了一个稳定的模型出口。接下来才是 SQLite 这一侧的正题。3. 可复制配置row_factory 设置与 Row 转 dict 的完整代码这一节是全文的技术核心所有代码都可以直接复制运行。先建一个测试库再演示 Row 对象的取值方式最后给出通用的转换函数。第一步建库建表并插入数据。注意这里故意先不设置 row_factory让你看到默认返回类型是什么import sqlite3 conn sqlite3.connect(test.db) c conn.cursor() c.execute(DROP TABLE IF EXISTS stocks) c.execute( CREATE TABLE stocks( data text, trans text, symbol text, qty real, price real ) ) c.execute(INSERT INTO stocks VALUES(2017-01-05,BUY,RHAT,100,35.14)) conn.commit()第二步设置 row_factory。这一行是分水岭设置之后所有查询返回的都是 sqlite3.Rowconn.row_factory sqlite3.Row c conn.cursor() c.execute(SELECT * FROM stocks) r c.fetchone() print(type(r)) # class sqlite3.Row print(tuple(r)) # (2017-01-05, BUY, RHAT, 100.0, 35.14) print(r[2]) # RHAT print(r.keys()) # [data, trans, symbol, qty, price] print(r[qty]) # 100.0 for field in r: print(field)运行结果会依次输出 Row 类型、元组形式、按整数下标取值、列名列表、按列名取值以及遍历每个字段的值。这里有个细节r[qty]返回的是100.0因为建表时 qty 是 real 类型SQLite 会把它存成浮点。如果你需要整数得在转换时做类型映射。第三步Row 转 dict。最直接的写法是用dict(row)因为 Row 支持迭代出键值对def row_to_dict(row): return dict(row) if row is not None else None d row_to_dict(r) print(d) # {data: 2017-01-05, trans: BUY, symbol: RHAT, qty: 100.0, price: 35.14}第四步多行查询转 list[dict]def rows_to_list(rows): return [dict(row) for row in rows] c.execute(SELECT * FROM stocks) all_rows rows_to_list(c.fetchall()) print(all_rows)第五步转 JSON。因为已经转成 dictjson.dumps就不会报错了import json print(json.dumps(all_rows, ensure_asciiFalse, indent2))如果你不想每次都手动转可以在连接级别做字段映射。下面这个配置片段把 row_factory 和类型转换封装在一起适合放进项目的 db 模块import sqlite3 import json class DictRow(sqlite3.Row): def to_dict(self): return dict(self) def get_conn(db_pathtest.db): conn sqlite3.connect(db_path) conn.row_factory DictRow return conn def query_all(conn, sql, params()): c conn.cursor() c.execute(sql, params) return [row.to_dict() for row in c.fetchall()] if __name__ __main__: conn get_conn() data query_all(conn, SELECT * FROM stocks WHERE symbol ?, (RHAT,)) print(json.dumps(data, ensure_asciiFalse, indent2)) conn.close()这段代码的好处是row_factory 统一在get_conn里设置业务代码只调query_all拿到的永远是 dict 列表后面接 TaoToken 的模型调用时直接json.dumps就能作为请求体的一部分。如果你用 TOML 管理配置可以这样写[database] path test.db row_factory sqlite3.Row [taotoken] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model_id your-model-id注意row_factory在 TOML 里只能写字符串实际代码里还是要映射成sqlite3.Row对象别直接把这个字符串塞给 connect。4. 验证请求与成功结果类型检查与结构化输出实测代码写完必须验证不然你不知道拿到的到底是 Row、dict 还是别的。这一节给出完整的验证步骤包括类型检查、字段映射检查和最终的结构化输出。先做类型检查。很多人以为dict(row)一定成功其实如果 row 是 Nonedict(None)会抛TypeError。所以转换函数里要判空def safe_row_to_dict(row): if row is None: return None if isinstance(row, sqlite3.Row): return dict(row) if isinstance(row, dict): return row raise TypeError(funsupported row type: {type(row)})然后写一个验证脚本把每一步的类型都打印出来import sqlite3 import json conn sqlite3.connect(test.db) conn.row_factory sqlite3.Row c conn.cursor() c.execute(SELECT * FROM stocks) r c.fetchone() print(row type:, type(r)) print(is Row:, isinstance(r, sqlite3.Row)) print(keys:, r.keys()) print(qty type:, type(r[qty])) d safe_row_to_dict(r) print(dict type:, type(d)) print(dict keys:, list(d.keys())) print(json ok:, json.dumps(d, ensure_asciiFalse))预期输出大致是row type: class sqlite3.Row is Row: True keys: [data, trans, symbol, qty, price] qty type: class float dict type: class dict dict keys: [data, trans, symbol, qty, price] json ok: {data: 2017-01-05, trans: BUY, symbol: RHAT, qty: 100.0, price: 35.14}看到json ok这一行说明 Row 到 dict 到 JSON 的链路完全打通。接下来把结构化数据接到 TaoToken 通道做一次真实请求。这里用 requests 演示Key 从环境变量读import os import json import requests api_key os.environ[TAOTOKEN_API_KEY] base_url os.environ.get(TAOTOKEN_BASE_URL, https://taotoken.net/api) payload { model: your-model-id, messages: [ {role: system, content: 你是一个数据整理助手请把输入的股票记录整理成一句话摘要。}, {role: user, content: json.dumps(d, ensure_asciiFalse)} ] } resp requests.post( f{base_url}/chat/completions, headers{ Authorization: fBearer {api_key}, Content-Type: application/json }, jsonpayload, timeout60 ) print(status:, resp.status_code) print(body:, resp.text[:500])成功的话 status 是 200body 里能看到模型返回的摘要内容。如果 status 是 401说明 Key 有问题如果是 404多半是 Base URL 拼错了如果是 400 且提示 model 相关说明 Model ID 不对。这些在下一节详细排。验证模型是否可用也可以直接在模型对话页面测试https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。把上面那段 JSON 粘进去看模型能不能正常返回能返回就说明通道没问题问题在本地代码。类型检查这一步别省。我见过有人把r.keys()的结果当成 list 直接索引结果拿到的是odict_keys视图对象r.keys()[0]直接报错。正确写法是list(r.keys())[0]或者r.keys()[0]在部分版本可用但不保险。统一转 list 最稳。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节按真实报错来排每个报错给出原因和修复方式。你遇到哪个就查哪个。报错一401 Unauthorized。最常见的原因是 Key 没读到或者写错了。先确认环境变量echo $TAOTOKEN_API_KEY如果输出为空说明没 export 成功。Windows 下用echo %TAOTOKEN_API_KEY%。如果 Key 有值但还是 401检查请求头是不是Authorization: Bearer key注意 Bearer 后面有一个空格。还有一种情况是 Key 被复制时带了换行或空格用strip()处理一下。报错二local proxy failed。这个报错通常出现在客户端工具里比如 Cline、Claude Code 或者某些 IDE 插件。原因是客户端配置了本地代理地址但代理服务没启动或者端口写错。修复方式是检查客户端的 Base URL 配置确保指向https://taotoken.net/api而不是http://127.0.0.1:xxxx。如果你在 settings 里看到 proxy 相关字段把它清空或者改成正确的统一入口。CC Switch 这类工具要写全三件套Base URL、Key、Model ID缺一个就可能走到错误的默认代理。报错三reading choices 相关。典型报错是KeyError: choices或者list index out of range。这说明你拿到的响应不是预期的 chat completion 结构。先打印resp.status_code和resp.text大概率是 4xx 错误被当成正常响应解析了。常见原因是 Model ID 写错服务端返回了错误 JSON里面没有 choices 字段。修复确认 Model ID 与控制台一致确认请求体里messages格式正确。报错四OAuth 相关。如果你用的是 Claude Code 或者 Codex 这类带 OAuth 流程的工具报错可能是OAuth token expired或者invalid_grant。这类工具建议直接用 API Key 模式而不是 OAuth。在配置里找到 auth 相关字段切换成 Key 认证。Codex 的auth.json要写全三件套{ base_url: https://taotoken.net/api, api_key: 你的Key, model: your-model-id }注意auth.json里的字段名以你所用工具的实际文档为准不同版本可能叫apiKey或api_key。改完重启工具。报错五Row 转 dict 后 JSON 序列化仍然失败。这种情况通常是 dict 里嵌套了非序列化类型比如datetime或者Decimal。SQLite 的 real 类型读出来是 float一般没问题但如果你存了 bytes 或者自定义类型就要在转换时处理def json_default(obj): if isinstance(obj, bytes): return obj.decode(utf-8, errorsreplace) raise TypeError(fnot serializable: {type(obj)}) json.dumps(data, ensure_asciiFalse, defaultjson_default)报错六row_factory 设置了但没生效。检查你是不是在connect之后、cursor之前设置的。如果先创建了 cursor 再改 row_factory已经创建的 cursor 不受影响。正确顺序是 connect → 设置 row_factory → cursor → execute。排障时记住一个原则先看 HTTP 状态码再看响应体最后看本地类型。顺序反了会浪费很多时间。6. 把本地 SQLite 结构化输出接到 TaoToken 的稳定工作流走到这里你已经有了完整的链路SQLite 建库、row_factory 设置、Row 转 dict、dict 转 JSON、通过 TaoToken 统一通道调用模型。最后说几个让这套流程更稳的实用技巧。第一把转换函数和数据库连接封装成独立模块业务脚本只 import不要每个文件都重写一遍dict(row)。这样以后要加字段映射或者类型转换只改一个地方。第二Key 永远走环境变量不要写进代码也不要提交到 git。如果你用.env文件记得加进.gitignore。第三模型调用加超时和重试。网络请求没有超时设置脚本可能卡死。requests的timeout参数至少给 30 秒重试可以用简单的循环或者urllib3的 Retry。第四结构化输出优先用 JSON。模型对 JSON 输入的理解通常比纯文本好而且你从 SQLite 转出来的 dict 天然适合json.dumps。如果字段很多可以在 system prompt 里说明字段含义模型返回会更准。第五验证模型通道是否正常最快的办法是去模型对话页面手动发一条消息https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。能正常回复说明 Key 和 Base URL 没问题剩下的就是本地代码的事。如果你需要管理多个 Key 或者查看调用情况控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。API Keys 管理页面是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 建议定期轮换 Key。长期跑编码和 Agent 任务的话Coding Plan 比按次调用更省心https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 遇到 SDK 用法问题先查文档。最后留一个可以直接跑的最小完整脚本把前面所有步骤串起来import os import json import sqlite3 import requests def get_conn(db_pathtest.db): conn sqlite3.connect(db_path) conn.row_factory sqlite3.Row return conn def query_all(conn, sql, params()): c conn.cursor() c.execute(sql, params) return [dict(row) for row in c.fetchall()] def call_model(records): api_key os.environ[TAOTOKEN_API_KEY] base_url os.environ.get(TAOTOKEN_BASE_URL, https://taotoken.net/api) payload { model: your-model-id, messages: [ {role: system, content: 整理以下记录为摘要。}, {role: user, content: json.dumps(records, ensure_asciiFalse)} ] } resp requests.post( f{base_url}/chat/completions, headers{Authorization: fBearer {api_key}, Content-Type: application/json}, jsonpayload, timeout60 ) resp.raise_for_status() return resp.json() if __name__ __main__: conn get_conn() records query_all(conn, SELECT * FROM stocks) print(records:, records) result call_model(records) print(model reply:, result[choices][0][message][content]) conn.close()把your-model-id换成你控制台里的实际 Model IDKey 配好环境变量直接python script.py就能跑。看到 records 打印出 dict 列表、model reply 打印出模型摘要整条链路就通了。