公司休年假,近10来天的假期,本来计划去西藏旅游的,因为疫情又起,最后放弃了,选择宅在家继续处理EFunction项目。很多朋友希望EFunction能够支持自定义函数开发工作,正好趁着这个假期研究下怎么利用Python开发自定义函数工作。

其实Python开发Excel自定义工具有很多,比如xlwings,pyxll等。通过研究对比发现xlwings和pyxll都是对pywin32工具做了封装二次开发。自己也依葫芦画瓢,利用pywin32开发自己的第一个自定义函数。

自定义函数BUG处理

因为我的Python开发环境通过anaconda安装,已经自带pywin32工具包,刚开始使用默认的pywin32工具包开发自定义函数,函数在引入numpy工具包或者第三方引用了numpy工具包的其他包,例如pandas时,最后使用自定义函数时,会报错。

numpy错误

因为这个问题,苦恼了很久。为了解决这个问题,翻阅N多帖子,都没有找到解决办法。后来瞎猫碰到死老鼠,把这个问题给解决了。

其实这个问题如果使用xlwings开发自定义函数,也会同样存在这个问题。最后解决这个问题方法很简单,只需要重新手动安装numpy工具包就可以,网络上帖子大多解释,该问题可能是anaconda环境引起。

划重点,不是所有的的anaconda环境都存在该问题,我台式机存在该BUG,但我笔记本就无该问题。如果没有该报错可以无需处理该问题。

在手动安装前,可以通过conda list命令查阅下,当前你的numpy版本是多少,记录下来。安装numpy前,先要卸载环境内numpy包,再安装同版本numpy。

先查阅 numpy版本

卸载完成后,再安装指定版本numpy,我的版本为1.21.6,安装命 令为:

pip3 install numpy==1.21.6 -i https://pypi.tuna.tsinghua.edu.cn/simple

以上准备后好,就可以进入下一个主题了,如果你目前暂且不使用numpy包,只想体验Python开发Excel自定义函数,该步骤可以跳过。直接进入下一个环节。

开发第一个自定义函数

以实际案例来开发第一个自定义函数,函数使用到EFunction,jupyter开发工具,启用工具后录入两段代码。

代码1:自定义函数主体类,后续有新增自定义函数需求,可以在该类内添加或者修改。样例代码为:

%%writefile filename.py
import pythoncom as pc
import win32com.client as cl
#import pandas as pd
import numpy as np
import jieba
from win32com.client import Dispatch
class PythonComTestObject:
_reg_clsid_ = '{BB58C07E-B9AD-4BC7-BB8C-01D2FF8FD4E9}' #replace this
_reg_clsctx_ = pc.CLSCTX_LOCAL_SERVER
_reg_progid_ = "PythonComTestObject"
_reg_desc_ = "A library for doubling things."
# a list of strings that indicate the public methods for the object. If they aren't listed they are considered private.
_public_methods_ = ['cutWord']
# double every value in array and return
def cutWord(self,data):
#the VARIANT array comes gets converted to an [[]] array
seg_list = jieba.cut(data, cut_all=False)
word_list = []
for a in seg_list:
word_list.append(a)
return "happy"
if __name__ == '__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(PythonComTestObject)

上述代码定义了函数类主体,有一个自定义函数cutWord,该函数功能为对文本数据进行分词,结果以数组方式返回。如果有新增函数,依葫芦画瓢,添加一个函数即可。

写好函数函数类后,将该段代码执行,执行完成后,就可以在同目录下生成一个“filename.py”同名Python脚本文件。用以下脚本执行该文件即可,即可实现函数com注册,注册成功后,就可以通过Excel VBA调用该函数了。

%run filename.py

执行成功后,jupyter notebook界面会提示注册成功提示。例如下图界面。

注册com组件

完成函数注册后,就可以在VBA之中调用了。其实调用脚本非常简单,大家可以用同一个模板,就可以,只需要做细微修改。

Function cut_word(x)
On Error GoTo MyErr
Set det = VBA.CreateObject("PythonComTestObject")
If IsObject(x) Then
cut_word = det.cutWord(x.Value)
Else
cut_word = det.cutWordArray(x)
End If
Exit Function
MyErr:
cut_word = Err.Description
End Function

VBA自定义函数模板,在VBA内调用Python函数

最后就可以在Excel内调用自定义的函数了。最终的分词结果为:

分词函数分词结果

总结下用pywin32开发Excel自定义函数方法流程:

Python自定义函数三部法

按照模板定义自己的函数,定义完成后,将jupyter notebook内的代码写入py文件内。如果不通过EFunction jupyter notebook管理自定义函数,也可以使用其他的IDE工具编写Python脚本。编写完成后,就是执行模板脚本,在EFunction jupyter notebook内,就使用%fun filename.py就可以。完成自定义函数com注册后,就可以在vba内调用了函数了。也可以参照模板。

如果Python自定义函数新增或者函数名有更改,需要重复上述步骤,如果只是更改函数内的脚本,不更改函数Python名及参数。不需要重复执行Py文件。

下一步骤研究怎样用EFunction jupyter notebook管理自定义函数