65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ExcelPython 从 Excel 调用 Python 代码

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2013年9月3日

CPOL

5分钟阅读

viewsIcon

107655

ExcelPython 是一个轻量级的 COM 库,它允许您从 Excel VBA 调用 Python 代码并操作 Python 对象。

引言

ExcelPython 是一个轻量级的 COM 库,它允许您从 Excel VBA(或任何支持 COM 的语言)调用 Python 代码并操作 Python 对象。在本文中,我将向您展示如何做到这一点。

背景

尽管 Excel 的霸主地位正受到免费(OpenOffice)或云(Google Sheets)替代品的挑战,但在办公环境中,Excel 仍然是用户友好计算和数据处理最广泛使用的应用程序。能够从 Excel 调用 Python 代码,通过利用广泛的库集合和强大的语言功能,极大地扩展了 Excel 的功能。

ExcelPython 是一个进程内 COM 库,它允许您加载 Python 模块、调用方法和操作对象。其他 Python/Excel 接口,如 PyXLLPython-Excel,它们都是出色的工具,但与 ExcelPython 在一些细微之处有所不同,因此它们的适用范围也略有差异。 ExcelPython 的优点在于它:

  • 免费(包括成本和许可证),
  • 小巧轻便,
  • 高度可嵌入:可以使用打包好的 Python 发行版,这样就可以分发电子表格而无需安装 Python,
  • 对最终用户透明:Python 是从 Excel VBA 调用启动的,因此一旦电子表格代码开发完成,就不需要额外的最终用户干预,
  • 通用:可以调用任何现有的 Python 模块或函数,并操作任何 Python 对象,事实上,ExcelPython 可以直接调用 Python 库,而无需编写任何额外的 Python 代码来包装它们成可调用的函数。

使用代码 

如果您想要一个非常基础的教程,展示如何使用该库提供的函数,请访问 Sourceforge 上的 ExcelPython 页面,在那里您可以在 wiki 部分找到一个入门的分步指南。

在本教程中,我们将研究如何使用 ExcelPython 来合并一组列表、对其值进行排序并删除任何重复项。这在 Python 中是一个非常直接的任务,只需要几行代码,而等效的 VBA 代码将更加冗长。

这是我们将要从 Excel 调用的 Python 函数

def merge_sort_unique(lists):
  s = set()
  for L in lists:
    s.update(L)
  return sorted(s)

在您选择的位置创建一个新文件夹,并将此脚本保存为名为 Methods.py 的文件。

让我们一步一步地看看如何从 Excel 工作表中调用此方法。启动 Excel,首先创建一些输入数据,我们将把这些数据传递给 Python 脚本,并将工作簿保存在与 Python 脚本相同的文件夹中。您的工作表应如下所示:

下一步是打开 VBA 并添加对 ExcelPython 的引用。要打开 VBA,请按 Alt+F11,然后转到 Tools | References...,如果您已正确安装 ExcelPython,则应该可以在对话框中选择它。

为了检查您现在是否可以引用 ExcelPython 库,请尝试在“立即窗口”(如果尚未显示,请按 Ctrl+G)中键入 ?PyVar(PyEval("1+2")),您应该会看到结果。

现在我们可以调用 Python 方法了。创建一个新模块(Insert | Module)并编写以下 VBA 代码:

Function msu(lists As Range)
    Set methods = PyModule("Methods", AddPath:=ThisWorkbook.Path)
    Set result = PyCall(methods, "merge_sort_unique", PyTuple(lists.Value2))
    msu = WorksheetFunction.Transpose(PyVar(result))
    Exit Function
End Function

让我们逐行分析这段代码。

Set methods = PyModule("Methods", AddPath:=ThisWorkbook.Path)

此语句等同于 Python 中的 import Methods。在此,我们加载 Methods 模块,该模块定义在 Methods.py 中,遵循 Python 模块名称与其定义所在的文件或文件夹名称相同的约定,并将其存储在 VBA 变量 methods 中。这意味着 VBA 函数现在有一个名为 methods 的局部变量,它实际上指向 Python 模块对象。请注意 AddPath 参数,它允许我们在尝试加载模块之前将额外的文件夹插入到 Python 路径的开头——在这种情况下,我们添加包含工作簿的文件夹,以便它能找到我们的 Methods.py 文件。

接下来,我们调用 Methods 模块中的 merge_sort_unique 方法:

Set result = PyCall(methods, "merge_sort_unique", PyTuple(lists.Value2))

ExcelPython 的 PyCall 方法接受一个有序参数列表,该列表使用 PyTuple 方法构建,以及一个可选的关键字参数列表,可以使用 PyDict 方法构建。此处的结果存储在 VBA 变量 result 中。

result 变量现在指向一个 Python 列表对象,因此不能直接放入工作表中(这会发生类型错误)。因此,作为最后一步,我们使用 PyVar 方法将 Python 列表对象转换为 VBA 数组。

msu = WorksheetFunction.Transpose(PyVar(result))

PyVar 方法会通用地将 Python 对象转换为其 VBA 等效对象(在可能的情况下),并可以选择提示所需的维度来将 Python 列表的列表转换为二维数组。有关更多详细信息,请参阅 ExcelPython 的 sourceforge wiki。

现在我们可以看看它是否有效了!在工作表中,创建一个数组块(通过选择多个单元格、输入公式并按 Ctrl-Shift-Enter)并使用公式 =msu(A1:C16)。您应该会看到以下内容:

如果您没有看到,请不要担心,ExcelPython 非常容易调试。对 VBA 代码进行以下修改:

Function msu(lists As Range)
On Error GoTo do_error
    Set methods = PyModule("Methods", AddPath:=PyPath)
    Set result = PyCall(methods, "merge_sort_unique", PyTuple(lists.Value2))
    msu = WorksheetFunction.Transpose(PyVar(result))
    Exit Function
do_error:
    msu = Err.Description
End Function

这样就可以捕获任何错误并将其显示在工作表中,您可以分析出问题所在。

最后请注意,函数在列表的开头返回了一个 0,即使它不在任何输入列表中。这实际上是 Python None 对象粘贴到工作表中的结果。为什么我们的函数在合并的元素中返回 None 呢?

原因是输入列表的长度不同,但被作为块传递,因此包含了 VBA Empty 值。这些值会被打包成 Python 中的 None,所以输入列表实际上包含了 None 元素。一个快速的修复方法是修改 Python 中 merge_sort_unique 的行为,使其从返回值中删除 None
def merge_sort_unique(lists):
  s = set()
  for L in lists:
    s.update(L)
  s.remove(None)
  return sorted(s)

请注意,您所要做的就是修改 Methods.py,保存它并按 F9 重新计算工作表 - ExcelPython 将负责自动重新加载模块,这极大地促进了 Excel 接口的 Python 脚本的交互式开发。

© . All rights reserved.