Python自定義函數(shù)實(shí)現(xiàn)Excel自動(dòng)化操作教學(xué)
前言
這一節(jié)要解決的核心問(wèn)題很明確:能不能在 Excel 里點(diǎn)一下按鈕,然后讓 Python 自動(dòng)完成整套數(shù)據(jù)處理流程?
前面學(xué)習(xí) xlwings 時(shí),我們更多是在理解 Python 如何讀取 Excel、寫(xiě)入 Excel,或者把 Python 函數(shù)包裝成 Excel 公式。但到了這一節(jié),重點(diǎn)就變成了另一種更貼近辦公自動(dòng)化交付的方式: 用 VBA 作為觸發(fā)入口,用 Python 作為處理引擎,用 Excel 作為結(jié)果展示界面。
這張圖展示了 Excel 按鈕觸發(fā)后,VBA、xlwings、RunPython 和 Python 代碼之間的協(xié)作關(guān)系。

從這張圖中我們可以看出,RunPython 的價(jià)值不是“在 Excel 里寫(xiě)一行公式”,而是把 Excel 變成一個(gè)操作面板:用戶只負(fù)責(zé)點(diǎn)擊按鈕,真正的數(shù)據(jù)清洗、統(tǒng)計(jì)處理、報(bào)表生成交給 Python 完成。
注意:如果只把 RunPython 當(dāng)成普通函數(shù)調(diào)用來(lái)理解,就會(huì)低估它的實(shí)際價(jià)值。它真正適合的是“流程型任務(wù)”,比如批量整理數(shù)據(jù)、生成報(bào)表、寫(xiě)入結(jié)果 Sheet、導(dǎo)出文件等。
建議閱讀方式:
- 先理解 RunPython 和 UDF 的區(qū)別
- 再看項(xiàng)目如何創(chuàng)建、Python 端如何寫(xiě) main()
- 最后重點(diǎn)看常見(jiàn)坑:模塊名、解釋器、宏安全
2. 先講清楚:RunPython 和 UDF 到底有什么區(qū)別?
很多新手第一次接觸 xlwings 時(shí),會(huì)把 UDF 和 RunPython 混在一起。這個(gè)地方必須先講清楚,否則后面的代碼會(huì)越看越亂。
UDF 的定位更像 Excel 公式。比如你在單元格里寫(xiě):=MYFUNC(A1)
它的使用方式和普通 Excel 函數(shù)類似,輸入一個(gè)值,返回一個(gè)結(jié)果,適合做單元格級(jí)別的計(jì)算。
RunPython 的定位更像“自動(dòng)化任務(wù)入口”。它通常由 VBA 宏、按鈕、菜單觸發(fā),執(zhí)行的是一整套 Python 流程,例如讀取數(shù)據(jù)、清洗數(shù)據(jù)、生成報(bào)表、寫(xiě)回結(jié)果。
這張圖展示了 RunPython 與 UDF 的核心區(qū)別:左側(cè)是公式計(jì)算,右側(cè)是流程自動(dòng)化。

從這張圖中我們可以看出, UDF 更適合“單點(diǎn)計(jì)算”,RunPython 更適合“流程執(zhí)行”。 如果你的需求只是根據(jù) A1 計(jì)算 B1,用 UDF 就夠了;但如果你要一鍵處理一整張表、一整個(gè)工作簿,RunPython 更合適。
| 對(duì)比項(xiàng) | UDF | RunPython |
|---|---|---|
| 使用方式 | 單元格公式 | VBA 宏 / 按鈕觸發(fā) |
| 適用場(chǎng)景 | 單值計(jì)算、公式擴(kuò)展 | 批量處理、流程自動(dòng)化 |
| 典型入口 | =MYFUNC(A1) | RunPython "import xxx; xxx.main()" |
| 結(jié)果輸出 | 返回到單元格 | 可寫(xiě)回多個(gè) Sheet / 文件 |
| 更適合誰(shuí) | 想擴(kuò)展 Excel 函數(shù)的人 | 想做自動(dòng)化報(bào)表的人 |
我的建議:新手不要一上來(lái)就糾結(jié)哪個(gè)更高級(jí),而是先判斷任務(wù)類型。只要任務(wù)是“按按鈕跑流程”,優(yōu)先考慮 RunPython;只要任務(wù)是“像公式一樣算結(jié)果”,優(yōu)先考慮 UDF。
3. 適用場(chǎng)景:什么時(shí)候應(yīng)該用 RunPython?
RunPython 最適合的不是“寫(xiě)一段炫技代碼”,而是把重復(fù)性的 Excel 工作變成穩(wěn)定流程。尤其是那些手工操作步驟多、容易出錯(cuò)、每周每月都要重復(fù)做的表,非常適合用 RunPython 改造。
常見(jiàn)場(chǎng)景包括:
- 一鍵生成報(bào)表:從原始數(shù)據(jù) Sheet 讀取數(shù)據(jù),清洗后寫(xiě)入 Report Sheet;
- 批量數(shù)據(jù)處理:多個(gè)工作表統(tǒng)一匯總、去重、篩選、統(tǒng)計(jì);
- 模板化交付:同事只打開(kāi) Excel,點(diǎn)按鈕即可生成結(jié)果;
- 復(fù)雜邏輯計(jì)算:Excel 公式難維護(hù),但 Python 代碼更清晰;
- 圖表與文件輸出:生成 PNG、PDF、HTML 報(bào)告,再回寫(xiě)路徑或結(jié)果。
不推薦的場(chǎng)景:如果只是兩個(gè)單元格相加、簡(jiǎn)單文本拼接、普通 VLOOKUP 能解決的問(wèn)題,沒(méi)必要強(qiáng)行上 RunPython。工具不是越復(fù)雜越好,能穩(wěn)定解決問(wèn)題才是目標(biāo)。
從辦公自動(dòng)化角度看,RunPython 的意義在于把 Excel 從“手工操作工具”升級(jí)為“流程觸發(fā)入口”。
4. 用 quickstart 創(chuàng)建項(xiàng)目:自動(dòng)生成 xlsm 和 py
要讓 RunPython 跑起來(lái),最省事的方法不是自己從零配置,而是使用 xlwings 提供的 quickstart 命令。
在目標(biāo)文件夾中打開(kāi)命令行,執(zhí)行下面的命令:
xlwings quickstart vba_runpython_demo
執(zhí)行后,通常會(huì)生成一個(gè)項(xiàng)目文件夾,里面包含一個(gè) Excel 宏工作簿和一個(gè) Python 腳本文件:
vba_runpython_demo/
├── vba_runpython_demo.xlsm
└── vba_runpython_demo.py
這張圖展示了 quickstart 命令執(zhí)行后,自動(dòng)創(chuàng)建 `.xlsm` 和 `.py` 文件的效果。

這一步的核心要點(diǎn)是: 讓 Excel 宏文件和 Python 腳本文件保持在同一個(gè)目錄下。 這樣 VBA 調(diào)用 Python 時(shí),模塊導(dǎo)入路徑最簡(jiǎn)單,后續(xù)排錯(cuò)成本也最低。
這里不要隨便改文件名。比如 Python 文件叫 `vba_runpython_demo.py`,那么 VBA 里導(dǎo)入模塊時(shí)也應(yīng)該寫(xiě) `import vba_runpython_demo`。文件名、模塊名、導(dǎo)入名不一致,是 RunPython 新手最常見(jiàn)的錯(cuò)誤之一。
5. Python 端:寫(xiě) main(),讓它負(fù)責(zé)完整處理流程
在 RunPython 場(chǎng)景里,我建議給 Python 腳本寫(xiě)一個(gè)明確的入口函數(shù),例如 `main()`。這樣 VBA 端只負(fù)責(zé)調(diào)用 `main()`,具體邏輯都放在 Python 里維護(hù)。
一個(gè)標(biāo)準(zhǔn)的 Python 處理流程通常包括三步:
- 從 Excel 讀取數(shù)據(jù);
- 使用 Python 進(jìn)行統(tǒng)計(jì)、清洗、匯總;
- 把結(jié)果寫(xiě)回 Excel 的 Report 表。
這張圖展示了 Python 端 `main()` 的處理流程:左側(cè)讀取原始數(shù)據(jù),中間執(zhí)行 Python 統(tǒng)計(jì)處理,右側(cè)寫(xiě)入 Report 結(jié)果表。

從這張圖中我們可以看出,`main()` 不應(yīng)該只是隨便放幾行代碼,而應(yīng)該承擔(dān)“流程入口”的職責(zé)。它負(fù)責(zé)組織整個(gè)任務(wù),讓代碼從讀取數(shù)據(jù)到輸出結(jié)果形成閉環(huán)。
下面是一份可直接理解的示例:
import xlwings as xw
def summarize_range(book: xw.Book, sheet_name: str, addr: str):
"""
讀取指定 Sheet 的指定區(qū)域,并計(jì)算總和、平均值、最大值、最小值。
"""
sht = book.sheets[sheet_name]
values = sht.range(addr).value
# 把 Excel 區(qū)域值統(tǒng)一展開(kāi)成一維列表
flat = []
if isinstance(values, list):
for row in values:
if isinstance(row, list):
flat.extend(row)
else:
flat.append(row)
else:
flat = [values]
nums = []
for v in flat:
try:
if v is None or v == "":
continue
nums.append(float(v))
except Exception:
pass
if not nums:
return None
return {
"count": len(nums),
"sum": sum(nums),
"avg": sum(nums) / len(nums),
"max": max(nums),
"min": min(nums),
}
def write_report(book: xw.Book, out_sheet: str, result: dict):
"""
將統(tǒng)計(jì)結(jié)果寫(xiě)回 Excel 的 Report 工作表。
"""
sht = book.sheets[out_sheet]
sht.range("A1").value = [
["指標(biāo)", "值"],
["數(shù)量 count", result["count"]],
["總和 sum", result["sum"]],
["平均 avg", result["avg"]],
["最大 max", result["max"]],
["最小 min", result["min"]],
]
def main():
"""
VBA 調(diào)用 RunPython 時(shí),推薦統(tǒng)一調(diào)用 main()。
"""
wb = xw.Book.caller()
src_sheet = "Sheet1"
src_range = "A1:A20"
out_sheet = "Report"
# 如果沒(méi)有 Report 表,則自動(dòng)創(chuàng)建
if out_sheet not in [s.name for s in wb.sheets]:
wb.sheets.add(out_sheet)
result = summarize_range(wb, src_sheet, src_range)
if result is None:
wb.sheets[out_sheet].range("A1").value = "沒(méi)有可用數(shù)值數(shù)據(jù)"
return
write_report(wb, out_sheet, result)
這段代碼的關(guān)鍵點(diǎn)在于 `xw.Book.caller()`,它代表當(dāng)前從 Excel 調(diào)用 Python 的工作簿對(duì)象。 也就是說(shuō),Python 不需要重新打開(kāi) Excel 文件,而是直接接管當(dāng)前正在運(yùn)行宏的這個(gè)工作簿。
推薦做法:把復(fù)雜邏輯拆成多個(gè)函數(shù),例如 `summarize_range()`、`write_report()`,最后由 `main()` 統(tǒng)一調(diào)度。這樣后續(xù)維護(hù)和擴(kuò)展會(huì)輕松很多。
6. VBA 端:用 RunPython 調(diào)用 Python 函數(shù)
Python 端寫(xiě)好以后,就需要在 Excel 的 VBA 里調(diào)用它。操作入口是:
Alt + F11 → 打開(kāi) VBA 編輯器 → 插入模塊 → 編寫(xiě)宏
最常用的寫(xiě)法如下:
Option Explicit
Sub RunPythonMain()
RunPython "import vba_runpython_demo; vba_runpython_demo.main()"
End Sub這行代碼里有兩個(gè)關(guān)鍵點(diǎn):
import vba_runpython_demo:導(dǎo)入同目錄下的 Python 模塊;vba_runpython_demo.main():調(diào)用 Python 文件里的main()函數(shù)。
注意模塊名:如果你的 Python 文件叫 `demo.py`,這里就應(yīng)該寫(xiě) `import demo; demo.main()`。如果文件名和模塊名對(duì)不上,很容易出現(xiàn) `ModuleNotFoundError`。
如果你想調(diào)用 Python 里的某個(gè)單獨(dú)函數(shù),也可以這樣寫(xiě):
def hello(name="YJlio"):
wb = xw.Book.caller()
wb.sheets[0].range("D1").value = f"Hello, {name}!"
VBA 端對(duì)應(yīng)寫(xiě)法:
Sub RunPythonHello()
RunPython "import vba_runpython_demo; vba_runpython_demo.hello('YJlio')"
End Sub實(shí)際做項(xiàng)目時(shí),我更推薦先固定一個(gè) `main()` 作為入口。 按鈕只調(diào)用 main(),具體業(yè)務(wù)邏輯全部放到 Python 里。 這樣 Excel 端會(huì)更干凈,Python 端也更容易版本管理。
7. 常見(jiàn)問(wèn)題與踩坑記錄:RunPython 三大坑位
RunPython 本身不難,真正難的是環(huán)境、路徑、宏安全這些外圍配置。很多時(shí)候代碼沒(méi)有錯(cuò),但就是跑不起來(lái),問(wèn)題往往出在下面三個(gè)位置。
這張圖展示了 RunPython 最容易翻車的三大坑位:模塊名、解釋器、宏安全。

從這張圖中我們可以看出,RunPython 的排錯(cuò)不能只盯著 Python 代碼本身,還要同時(shí)檢查 Excel 宏環(huán)境、Python 解釋器路徑、模塊導(dǎo)入路徑。
7.1 模塊名錯(cuò)誤:ModuleNotFoundError
常見(jiàn)報(bào)錯(cuò)類似:
ModuleNotFoundError: No module named 'vba_runpython_demo'
常見(jiàn)原因:
.xlsm和.py不在同一個(gè)目錄;- Python 文件名改了,但 VBA 里的 import 沒(méi)改;
- Python 文件名包含特殊字符或空格;
- 當(dāng)前路徑?jīng)]有被加入 Python 搜索路徑。
推薦處理:新手階段盡量保持 .xlsm 和 .py 同目錄,并且不要隨意改名。
7.2 解釋器錯(cuò)誤:Excel 調(diào)用的 Python 不是你裝包的那個(gè)
常見(jiàn)報(bào)錯(cuò)類似:
No module named xlwings No module named pandas
這類錯(cuò)誤不是包真的沒(méi)裝,而是 Excel 調(diào)用的 Python 解釋器和你命令行里裝包的解釋器不是同一個(gè)。
處理思路:
在命令行確認(rèn)當(dāng)前 Python 路徑:
where python python -c "import sys; print(sys.executable)"
在 xlwings 配置中確認(rèn) Excel 使用的解釋器路徑;
把解釋器改成你真正安裝了 xlwings、pandas 的那個(gè)環(huán)境。
不要盲目重復(fù) pip install。先確認(rèn) Excel 調(diào)用的是哪個(gè) Python,否則你可能一直把包裝到 A 環(huán)境,而 Excel 卻在用 B 環(huán)境。
7.3 宏安全策略:按鈕點(diǎn)了但沒(méi)有任何反應(yīng)
在公司電腦上,宏安全經(jīng)常是 RunPython 跑不起來(lái)的原因之一。尤其是從外部下載、拷貝、郵件接收的 `.xlsm` 文件,可能會(huì)被 Excel 阻止宏運(yùn)行。
可以檢查:
- 文件是否被標(biāo)記為來(lái)自 Internet;
- Excel 宏是否啟用;
- 文件是否位于受信任位置;
- 公司是否有組策略禁用了宏;
- 是否需要 IT 側(cè)放行。
企業(yè)環(huán)境不要隨便降低宏安全級(jí)別。正確做法是確認(rèn)文件來(lái)源、放入受信任位置,或者按照公司安全規(guī)范申請(qǐng)白名單。
8. 效果驗(yàn)證:不要只看“沒(méi)有報(bào)錯(cuò)”
很多人寫(xiě)自動(dòng)化腳本,只要運(yùn)行沒(méi)有報(bào)錯(cuò),就認(rèn)為成功了。這個(gè)判斷太粗糙。RunPython 的驗(yàn)證至少要看三個(gè)層面:
8.1 VBA 是否成功觸發(fā) Python
可以在 main() 里先寫(xiě)一個(gè)最小測(cè)試:
def main():
wb = xw.Book.caller()
wb.sheets[0].range("A1").value = "RunPython 調(diào)用成功"
如果 A1 能寫(xiě)入內(nèi)容,說(shuō)明 VBA 到 Python 的調(diào)用鏈路通了。
8.2 Python 是否正確讀取數(shù)據(jù)
可以臨時(shí)把讀取到的數(shù)據(jù)寫(xiě)到另一個(gè)單元格,確認(rèn)讀取范圍沒(méi)有錯(cuò)。
sht.range("H1").value = values
8.3 Report 是否正確生成
最后要檢查 Report 表是否存在,統(tǒng)計(jì)結(jié)果是否符合原始數(shù)據(jù),尤其是總和、平均值、最大值、最小值是否準(zhǔn)確。
成功標(biāo)準(zhǔn):
- 點(diǎn)擊按鈕后沒(méi)有報(bào)錯(cuò);
- 指定 Sheet 的數(shù)據(jù)被正確讀取;
- Report 表自動(dòng)生成或更新;
- 統(tǒng)計(jì)結(jié)果和手工計(jì)算一致;
- 再次運(yùn)行不會(huì)重復(fù)制造臟數(shù)據(jù)。
真正可靠的自動(dòng)化,不是跑一次成功,而是反復(fù)運(yùn)行仍然穩(wěn)定。
9. 我的總結(jié)提升
這一節(jié)的核心,不是簡(jiǎn)單記住 `RunPython` 這一句 VBA 代碼,而是要理解它在 Excel 自動(dòng)化體系里的位置。
我對(duì) RunPython 的定位是:Excel 負(fù)責(zé)交互,VBA 負(fù)責(zé)觸發(fā),Python 負(fù)責(zé)處理,Report 負(fù)責(zé)交付。
這和普通 Excel 腳本不一樣。普通腳本可能只是幫你少點(diǎn)幾下鼠標(biāo),而 RunPython 更適合做成一個(gè)穩(wěn)定的“小型自動(dòng)化系統(tǒng)”。
我建議重點(diǎn)記住這幾條:
- UDF 是公式思維,RunPython 是流程思維;
- quickstart 能降低項(xiàng)目創(chuàng)建成本;
- Python 端建議統(tǒng)一寫(xiě) main() 作為入口;
- VBA 端只負(fù)責(zé)觸發(fā),不要堆復(fù)雜業(yè)務(wù)邏輯;
- 模塊名、解釋器、宏安全是三大排錯(cuò)重點(diǎn);
- 驗(yàn)證結(jié)果不能只看有沒(méi)有報(bào)錯(cuò),還要看數(shù)據(jù)是否正確寫(xiě)回。
如果只會(huì)復(fù)制 `RunPython "import xxx; xxx.main()"`,但不知道 Excel、VBA、Python、解釋器、宏安全之間的關(guān)系,真實(shí)辦公環(huán)境里一定會(huì)卡住。
所以我更建議把這一節(jié)當(dāng)成“Excel 自動(dòng)化交付方式”的升級(jí)點(diǎn),而不是單純當(dāng)成一個(gè)語(yǔ)法點(diǎn)。
以上就是Python自定義函數(shù)實(shí)現(xiàn)Excel自動(dòng)化操作教學(xué)的詳細(xì)內(nèi)容,更多關(guān)于Python Excel自動(dòng)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Python?Excel自動(dòng)化插入各種類型的公式和函數(shù)的完整指南
- Python自動(dòng)化實(shí)現(xiàn)批量打印Excel工作簿的完整指南
- Python實(shí)現(xiàn)Excel新舊格式互轉(zhuǎn)的自動(dòng)化方案
- Python自動(dòng)化拆分Excel工作表的實(shí)戰(zhàn)教學(xué)
- Python自動(dòng)化批量排序Excel所有工作表的完整指南
- Python自動(dòng)化篩選Excel工作簿中多個(gè)工作表的實(shí)戰(zhàn)教學(xué)
- Python自動(dòng)化實(shí)現(xiàn)對(duì)多個(gè)Excel工作簿中的工作表進(jìn)行分類匯總
- Python自動(dòng)化將Excel表格插入Word文檔的三種實(shí)用方案
- Python自動(dòng)化復(fù)制Excel sheet表的兩種實(shí)用方案
相關(guān)文章
關(guān)于Python中Inf與Nan的判斷問(wèn)題詳解
這篇文章主要介紹了關(guān)于Python中Inf與Nan的判斷問(wèn)題,文中介紹的很詳細(xì),對(duì)大家具有一定的參考價(jià)值,有需要的朋友們下面來(lái)一起看看吧。2017-02-02
詳解python爬蟲(chóng)系列之初識(shí)爬蟲(chóng)
這篇文章主要介紹了python爬蟲(chóng)系列之初識(shí)爬蟲(chóng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
教你使用python實(shí)現(xiàn)微信每天給女朋友說(shuō)晚安
非常棒的一個(gè)python小實(shí)戰(zhàn),文章主要教大家如何用python實(shí)現(xiàn)微信每天給女朋友說(shuō)晚安,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
Python 專題一 函數(shù)的基礎(chǔ)知識(shí)
本文從系統(tǒng)提供的內(nèi)部函數(shù)、第三方提供函數(shù)庫(kù)+簡(jiǎn)單爬出代碼及安裝httplib2模塊過(guò)程和用戶自定函數(shù)三個(gè)方面進(jìn)行講述。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-03-03
python對(duì)json的相關(guān)操作實(shí)例詳解
這篇文章主要介紹了python對(duì)json的相關(guān)操作,結(jié)合實(shí)例形式詳細(xì)分析了json的概念、功能以及Python針對(duì)json的解析、輸出、排序、轉(zhuǎn)換等操作技巧,需要的朋友可以參考下2017-01-01
Python實(shí)現(xiàn)從多表格中隨機(jī)抽取數(shù)據(jù)
這篇文章主要介紹了如何基于Python語(yǔ)言實(shí)現(xiàn)隨機(jī)從大量的Excel表格文件中選取一部分?jǐn)?shù)據(jù),并將全部文件中隨機(jī)獲取的數(shù)據(jù)合并為一個(gè)新的Excel表格文件的方法,希望對(duì)大家有所幫助2023-05-05
利用pipenv和pyenv管理多個(gè)相互獨(dú)立的Python虛擬開(kāi)發(fā)環(huán)境
這篇文章主要介紹了利用pipenv和pyenv管理多個(gè)相互獨(dú)立的Python虛擬開(kāi)發(fā)環(huán)境,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
淺談Python調(diào)用Shell腳本的三種常用方式
本文介紹了Python調(diào)用Shell腳本的三種常用方法,包括os.system、subprocess.run和subprocess.Popen,具有一定的參考價(jià)值,感興趣的可以了解一下2025-11-11

