夜雨聆风学习资料网

ARTICLE · 1259150

真正本地运行的Python for Excel &Wps,快到飞起!

真正本地运行的Python for Excel &Wps,快到飞起!

先说个前情提要。

前阵子我做了个 Excel 插件叫 SQL Lab,后来借着 AI 的劲,又做了 ExcelAI 和 SQL Editor。最近这个是第四个:能直接在本地跑 Python 的表格插件——PyXL。

PyXL,Python for Excel。这里的 Excel 现在是个统称——Excel 和 WPS 表格共用同一套侧栏、同一套读写接口。本来想叫 PyXLL,那已经是别人家的成熟商业插件了,所以我去掉一个 L。名字像,功能上是两码事,体验上更接近我做的 SQL Editor。

← 向左滑动 (前三款插件的 Excel 端界面)

SQL Lab

ExcelAI

SQL Editor

一、先回答一个问题:微软不是已经有 Python in Excel 了吗

熟悉表格的读者大概率会问这一句。是的,Microsoft 365 自带 Python in Excel,而且做得不错:支持 list 这类 Python 对象的折叠显示,也有一个像样的代码编辑器。

Microsoft 365 自带的 Python in Excel

那我为什么还要单独做一个?因为它有两个绕不过去的硬伤:

1. 它跑在云端。计算要联网,敏感数据你不敢往上传;数据量一大,等待时间几乎无解。

2. Python 能力被阉割。不能操作本地文件,只能处理工作簿里已经有的信息。

国产办公软件这边也差不多。WPS 的工具选项卡下确实有 PY 代码编辑器,但启用它的第一步,还是把文档转成在线文档。

WPS 的 PY 代码编辑器,第一步仍是转为在线文档

绕了一圈,结论反而更清楚了。我要的东西特别朴素:在本机跑,能读本地文件,结果直接落回表格。

打个比方:云端那套像一辆只能在园区里开的车,油门刹车都挺好用,就是出不了大门。

同样是 Python in Excel,方向完全不同

左边跑在别人的服务器上,右边跑在你自己的电脑里

Microsoft 365 · Python in Excel

计算发生在云端

✕计算在云端,全程需要联网

✕敏感数据要离开你的电脑

✕数据量一大,等待几乎无解

✕不能操作本地文件

敏感数据 / 大批量 → 不好使

PyXL · 本机 Python

Excel / WPS 里,计算发生在你的电脑

✓计算在本机,可以完全离线

✓数据不出本机

✓百万行写入 3.81 秒

✓能直接读 dbf / csv / parquet

离线 / 本地文件 / 大数据 → 都能打

注:两者是不同产品,此处只对比运行位置与能力边界。

那为什么不直接用我自己的 SQL Editor?因为 SQL 只能表达 SQL 能表达的东西。SQL Editor 背后是 DuckDB,写 SQL 是快得离谱,可一旦要调私有库、写复杂的清洗逻辑、读一个很少见的格式,SQL 就比较受限了。

所以这俩不是替代关系。后面速度那节你会看到,它们各有各的赢面。

二、把门槛抹平:环境这件事,我做成一键装

既然要本机跑,电脑上就得有 Python,还得装上 Polars、pandas 和一堆依赖。对普通 Excel / WPS 用户来说,光这一步就劝退;而会写 pandas 的人又会觉得:我在 VSCode 里写不香吗,干嘛在表格里手搓代码?

所以两件事得分开说清楚:

第一,代码怎么写。插件只负责辅助——补全、参数提示、悬浮说明、代码诊断,这些是刚需。至于写什么逻辑,插件不管;卡住了,把需求丢给 AI 也完全够用。

第二,环境怎么装。全塞进安装包体积会爆炸,也没必要。所以我另做了一个离线环境补充包:勾选组件,一键装完,不改插件代码和设置。

离线环境补充包:Python、Polars、pandas、Node、.NET、WebView2 一个窗口勾完

三、界面:你的分析工作台,就在表格旁边

这一段是重点。在 Excel 或 WPS 的「开始」选项卡点一下 PyXL,侧栏就出来了。整个用法可以概括成一句话:先选好结果要放在哪儿,再写代码。

下面这张是 Excel 端的真界面截图:读一个 parquet 文件,取 100 万行,侧栏里已经把结果算好了——1000001 × 19(含表头)。

Excel 端侧栏:顶部是环境与目标,中间写代码,下面出结果

同一套侧栏,在 WPS 表格里:环境行、四个按钮、结果区都在原位

侧栏按功能分六块,从上往下走一遍,基本就会用了。

侧栏就六块,从上往下走一遍

对照上图,编号即功能区块

01 / ENV

环境状态行

Python 3.15.0 + Polars 2.0.0 + pandas 3.0.6。点这一行能扫描解释器、选虚拟环境,还能改执行模式和超时。

02 / TARGET

工作簿与目标

当前工作簿「读取测试.xlsx」,结果起点 'Sheet1'!$A$6。结果从这个格子开始向右、向下自动铺开。

03 / RUN

四个运行按钮

执行写入、预览、执行一次、停止。Ctrl+Enter 执行写入,Ctrl+Shift+Enter 预览,别按错。

04 / SAVE & PICK

保存与插入选区

Ctrl+S 保存草稿和工作簿;「插入选区」去表里框一下,引用自动填进 xl.read。

05 / EDITOR

代码编辑器

成员补全、参数提示、悬浮说明、代码诊断,全部跟着你选的那个 Python 环境走。

06 / RESULT

结果与问题

结果能切成表格和 Python 文本两种看法;print 和报错在「输出」;诊断在「问题」,点一下跳到对应行。

界面截图为真实运行画面(Excel 端),编号为本文导览标注。

六块之外还有一条容易被忽略、但很有用的东西:底部状态栏。它显示这次公式的耗时,悬浮还能拆开看 Python 执行、结果转换各花了多久。后面讲速度要用到它。

四、第一个结果:把订单明细变成地区汇总

四步走完,就能出第一个结果:

第一步,在「汇总」工作表选一个空白单元格,比如 A1;

第二步,点「插入选区」,去框选订单区域,确认后自动回到原目标;

第三步,写下分析逻辑——按地区分组,把金额加起来;

第四步,先预览检查,再点「执行写入」,表格会从目标格展开完整结果。

import polars as pldf = xl.read("订单!A1:C6")result = (    df.group_by("地区")      .agg(pl.col("金额").sum())      .sort("金额", descending=True))xl.write(result)

习惯 pandas 也一样能跑,写法换一套就行(需要在所选的 Python 环境里装好 pandas)。

三个按钮,三种交付方式(最容易搞混的地方)

区别只有一个:输入改了以后,结果跟不跟着变。

执行写入:把代码变成单元格里的公式,以后输入一动,自动重算。适合持续更新的报表。

预览:代码真的会跑,但结果只停在侧栏,工作表一个字都不动。适合探索和检查。

执行一次:把这一刻的结果写成普通数值,公式不保留。适合最终交付和结果快照。

一句话记住:想让它活着用「执行写入」,只是想看看用「预览」,想让它定格用「执行一次」。

一共三个按钮,结果交付方式完全不同

区别只有一个:输入改了以后,结果跟不跟着变

01 / LIVE

执行写入

保存逻辑 · 自动重算

代码变成单元格里的公式,输入一动就重跑一遍,结果自动更新。

适合

持续更新的日报 / 周报

=PYXL("Python 代码")

02 / PREVIEW

预览

侧栏查看 · 不写表

代码真的会跑,但结果只停在侧栏,工作表一个字都不动。

适合

探索数据 / 检查结果

侧栏快照 · 不写表

03 / ONCE

执行一次

固定值 · 再点才更新

把这一刻的结果写成普通数值,公式不保留。别人不装插件也能看。

适合

最终交付 / 结果快照

固定值 · 公式不保留

五、原理:把「怎么算」留在单元格里

点完「执行写入」,公式栏里保存的就是那段 Python 代码。插件记住它读了哪些区域;这些输入一变,就重新取数、重新算,再把结果交回表格。

完整走一遍是四步:记住数据位置 → 输入变了重新取数 → 在本机跑 Python → 从起点展开结果。

把「怎么算」留在单元格里:一次重算走四步

公式记住的是引用关系,所以区域移动、改名、表扩展都能跟住

01

记住数据位置

识别代码里直接写出的区域引用,和工作簿建立对应。

xl.read("订单!A1:C6")

→

02

输入变了重新取数

读取输入当前的计算值,组成这次分析的数据快照。

工作表 → DataFrame

→

03

在本机跑 Python

用你选的环境执行清洗、关联、分组这些逻辑。

Polars / pandas

→

04

从起点展开结果

表格把结果铺到右方和下方;下次输入变化再更新。

汇总!A1 → 动态数组

这一步的意义在于:从「把表格处理一遍」,变成「把处理方法留下来」。下次数据更新,你不用从头再来一遍。

两个坑提前说:

① 公式里请直接写 xl.read("区域") 这样的字符串常量,动态拼接的引用只用于预览或执行一次;

② 结果区要留够空白,被别的内容挡住,宿主会给你一个 #SPILL!。

六、只有两个动作要记:xl.read 和 xl.write

PyXL 的接口小得有点过分,表格和 Python 之间只有两个动作:xl.read 把表格变成能分析的数据,xl.write 把结果带回表格。

表格和 Python 之间,只需读懂这两个动作

一个把表格变成数据,一个把结果交回表格

↓ INPUT

xl.read · 把数据读进来

单元格 / 连续区域

"订单!A1:C6"

工作簿中的名称

"销售数据"

Excel 表(Table)

"订单表[#All]"

动态数组结果

"结果!A1#"

一个引用 = 一张内存里的表;多个引用 = 多个独立的表

PYTHON

→

中间随你写

↗ OUTPUT

xl.write · 按需要返回

DataFrame(最常用)

Series

标量,比如 42

规则的二维列表

headers=True 要列名

transpose=False 不转置

一次执行最多调用一次 xl.write

读多个区域,就能在一次分析里完成关联和追加。订单表里只有客户编号?再读一张客户表,Polars 用 join、pandas 用 merge,名称就补上了。

想更精确地控制读取和输出,xl.read() 函数还有几个开关:headers=False 把首行也当数据;schema={"金额": pl.Float64} 只转换指定列,其余列继续自动推断。

数据转换的边界也处理过:空白读进来保留缺失值、写回显示为空白;超出 15 位数字精度的整数按文本输出;00123 和以 = 开头的字符串保留文本语义,不会被宿主自作主张。

七、写得顺手的地方

前面讲的是主干:怎么打开、怎么写、结果怎么回来。真正每天用起来,决定手感的是下面这些零碎的东西。它们不算卖点,但少一样就会觉得别扭。

· 写代码像在熟悉的编辑器里:补全、签名提示、诊断都在,中文右键菜单、查找替换、Ctrl + 滚轮缩放也都有。

· 每个单元格都有自己的草稿,切换时可以「保留并切换」或「不保留并切换」。多个窗格对同一个格子写了不同内容,保存会停下来,不悄悄覆盖。

·  xl.read() 函数可以只定一列的数据类型:schema={"金额": pl.Float64},其余列继续自动推断。

· 外观自动跟随软件的浅色、深灰或黑色主题,代码和草稿在切换时保留。

· 结果落地前先检查目标区域:保护、合并、边界都过一遍,已有内容会先问你要不要覆盖,取消就一个字都不改。

八、速度:3 秒级写进 100 万行,读 dbf 反超 4 倍

细节说完,回到插件最硬核的亮点,也是我最想讲清楚的部分。

先交代一句:PyXL 没有引用任何开源插件引擎,执行链路是自己写的——Rust 原生运行时负责调度和 Arrow / Grid 转换,Python 侧只管你的分析逻辑。目标很明确:速度优先。

但快不快,得看干什么活。我把话说明白,有赢有输。

① 百万行写入:略逊 0.73 秒

同一个 parquet 文件,100 万行 × 19 列,将近 1900 万个单元格,两边都是刚打开宿主的第一次(也就是最慢的那次)。

SQL Editor(DuckDB)3.08 秒,PyXL 3.81 秒。慢 0.73 秒,这个我认。

但 3.81 秒是什么概念?100 万行 × 19 列,将 1900 万个单元格,三秒多全铺进工作表,平均每秒 26 多万行。这个量级上,慢 0.73 秒真不算事——两边都是「点一下、等一下、好了」的水平,都快。

① 百万行写入工作表

Excel 端 · 100 万行 × 19 列 · 同一台机器 · 首次冷启动 · 越短越好

SQL Editor

DuckDB

3.08 秒

PyXL

本机 Polars

3.81 秒

慢 0.73 秒。但 3.81 秒意味着每秒写进约 26 万行、500 万个单元格——两边都是「点一下、等一下、好了」的水平。

Excel 端 · SQL Editor:1000000 行 × 19 列,耗时 3.08 秒

Excel 端 · PyXL:1000001 行 × 19 列,公式耗时 3.81 秒

② 读 dbf:2.19 秒 vs 9.19 秒

dbf 是老系统和行业软件常见的导出格式,我工作中经常要读。DuckDB 读它得走扩展,一次 757,771 行 × 8 列:SQL Editor 9.19 秒,PyXL 2.19 秒。

快了整整 7 秒,约 4.2 倍,这就有速度上体感差异了!

② 读取 dbf 数据库文件

Excel 端 · 757,771 行 × 8 列 · 同一台机器 · 首次冷启动 · 越短越好

SQL Editor

read_dbf 扩展

9.19 秒

PyXL

klaw_dbase

2.19 秒

同一条数据链路,PyXL 省掉了「先加载扩展、再解析」的往返,读本地文件直接反超。

Excel 端 · SQL Editor:757771 行 × 8 列,耗时 9.19 秒

Excel 端 · PyXL:757772 行 × 8 列,公式耗时 2.19 秒

为什么能快?四个技术点

1. 内嵌执行,省掉跨进程。Python 就跑在表格进程里,同一进程内共享 CPython 模块。数据不用在进程之间搬一趟,这一整段开销直接消失。

2. Arrow 直通,不做逐格处理。Polars 的结果本来就是列式的,经 Arrow C Data 直接转成原生结果网格,而不是「一格一格往里塞」。

3. 通知通道不扛数据。RTD 只通知一个「结果已就绪」的标量,宿主主线程随后从原生缓存里整块取走完整数组。1900 万个单元格,不走通知通道。

4. 外部模式也不亏。每本工作簿一个独立进程,JSON 分块转成原生网格;勾上「高性能入口」,还能少构造一次 .NET 二维数组。

结果回表格的两条路

执行位置和公式结果返回方式是分别配置的

EMBEDDED

内嵌高性能

表格进程内共享 CPython,结果走 Arrow C Data → 原生结果网格 → 表格。

要求:Windows x64 · CPython 3.13.7 / 3.15.0 · Polars 2.0.0

EXTERNAL

外部兼容

每本工作簿一个独立进程,JSON 分块转原生网格。跑飞了可以强制停止。

适合已有的 x64 Python / Polars 环境

还有「自动」:执行前探测资格,通过则内嵌,否则外部。只在代码运行前选一次路,脚本失败不会换路重跑。WPS 端采用宿主管理的公式返回路径,不显示 Excel 专用的「高性能入口」开关。

口径也得说清楚

状态栏那个「公式耗时」,不等于 Python 执行时间。它包含依赖分析、取数传输、排队和展开——比只看 Python 那一段更接近你真实等待的时间。悬浮上去能拆开看。

百万行基准的验收也是按整表核的:A1:S1000001 全区域、19 列的标题和类型、首/中/末行全部列,还要确认用户代码恰好执行了一次。不是看个大概。

最后一句诚实话:WPS 端同样的百万行,首次跑下来 4.84 秒。宿主不同,WPS 走的是宿主管理的公式返回路径,比 Excel 内嵌慢一些。这个数字我也不打算藏。

九、开发侧记:一周把 WPS 也接上了

上一节最后那个 4.84 秒,其实是最近一周才有的数字。这一节不谈功能,记一段开发过程——PyXL 怎么从单宿主走到双宿主。

PyXL 起初只做了 Excel 端。最近一周 Codex 疯狂重置,我干脆启动了「WPS 双宿主实施计划」——先让 AI 把宿主兼容层的边界摸清楚,再分阶段改。

它给出的判断挺准:这不只是加一个 WPS 注册项,Excel-DNA、COM、原生 XLL 和任务窗格,四样都得有对应的宿主适配。

先出计划再动手:一份能照着执行的实施方案,而不是一句「支持 WPS」

接下来那几天,Token 是肉眼可见地往下掉。一个阶段跑完接下一个,中间几乎没停过——不是我在那儿一行行敲,而是把边界讲清楚、让 AI 出改动、我逐项验收,来回几轮,Token 就成片地烧。

TOKEN BURN / ONE WEEK

6 个阶段 · 29 项任务 · 11 组验证场景

这一周真正烧掉的是 Token:AI 出改动,我出边界和验收。手写的代码没多少,对齐的次数不少。

等 Token 烧得差不多,WPS 版本也跑起来了——就是第三节那张截图里的样子:公式栏里同样是 =PYXL(...),和 Excel 端是同一段代码,结果一样从起点铺开。

同一套编辑器、同一套执行实现。安装向导里勾 Excel、WPS 或两者;公式、草稿和引用元数据随支持的工作簿保存,已验证的 Excel ↔ WPS 往返能保留代码和引用并重算。

同一套侧栏,两种宿主

装一次,向导里勾选 Excel、WPS 或两者

SHARED

两端完全一致

· 编辑器、补全与诊断

· xl.read 与 xl.write

· 执行写入 / 预览 / 执行一次

· 草稿、公式与引用随工作簿保存

DIFFERENT

两端的差别

· 结果返回:Excel 可选「高性能入口」,WPS 走宿主管理的路径

· 安装:向导里分别勾选要启用的宿主

· 环境:换已绑定的 Python 需重启当前宿主

WPS 支持 64 位版本,不限定具体版本号。

边界也说清楚:正式范围是 Windows x64 的 Excel / WPS 共存环境;纯 WPS 系统的验证延后,Mac 暂未纳入。

十、装之前,先确认这几样

· 表格软件:Windows 64 位 Excel(需支持动态数组)/ 64 位 WPS

· Python 环境:本机 x64 Python + Polars(pandas 按需装)

· 运行组件:.NET 8 Desktop Runtime x64 + WebView2

· 代码提示:Node.js 22 及以上

不想自己折腾环境的,装那个离线补充包就行;装之前记得先关掉全部 Excel / WPS 窗口和后台进程。

公式和草稿能随 .xlsx / .xlsm 一起保存。但对方要重新计算,也得装 PyXL 和对应环境;只想把结果给人看,就用「执行一次」——它写的是普通数值,不装插件也照样能看。

十一、几个容易被误解的细节

预览也会真的跑 Python。只是不通过 xl.write 写表。脚本里如果有写文件、联网、改数据库的操作,照样会发生,跑之前先看清楚代码。

保存 ≠ 执行写入。Ctrl+S 只保存草稿和工作簿;点「执行写入」才变成公式,之后还得保存工作簿,公式才留在文件里。

手动计算模式下要按 F9。Python 异步完成时,可能还要再按一次。

代码直接显示在公式栏。受宿主公式长度上限约束(Excel 为 8192 个字符)。

离线能用。环境和依赖准备好、脚本只用本地数据时全程不联网;但你的代码主动访问网络,它还是会联网。

读取限当前工作簿内。支持跨工作表,不支持跨工作簿。

重开工作簿不会自动跑。先保留上次结果;输入变了或再点「执行写入」才更新。

最后

做 PyXL 的初衷其实很小:我不想再为了学 pandas 或 polars,把数据在表格和编辑器之间搬来搬去。

做着做着,它变成了一件稍微大一点的事:表格负责摆数据,Python 负责把「怎么处理」讲清楚。公式留在单元格里,下一次数据更新,你不用从头来一遍。

而且它算在你的电脑上。你的数据还是你的数据,一行都不用离开这台机器。

从一个区域、一段看得懂的代码、一份能自动更新的结果开始。

PyXL,让表格多一种解题方式。

更多介绍请移步阅读原文

相关学习资料