有没有办法在Excel-VBA中调用Python代码?

时间:2017-07-31 07:58:56

标签: python excel vba excel-vba

我有一个包含宏的Excel文件(Main.xlsm)。我有一个Python文件(python.py)来生成一个辅助Excel文件(sub.xlsx),我将在Main.xlsm文件的宏中进一步调用它。这个由python.py运行生成的sub.xlsx文件保存在同一个工作目录中。

现在我想让这个python.py在Main.xlsm宏运行期间执行,然后使用这个xlsx文件。我基本上想要减少外部执行python.py的步骤。那是否有命令?我是VBA的新手。

4 个答案:

答案 0 :(得分:26)

最简单的方法是使用Shell命令

运行python解释器
Shell ("python.exe " & yourScript & " " & arguments)

答案 1 :(得分:19)

是的,有。我这样做的首选方法是通过xlwings(https://www.xlwings.org/),但也有其他一些选项。 XlWings很棒,因为它是免费的,开源的,易于使用的,具有出色的文档。但是有一些功能限制,所以你必须检查它是否符合你的需要。

答案 2 :(得分:15)

使用VBA运行python脚本有多种方法,具体取决于您是否需要等待执行结束并知道它是否没有错误。

使用Shell,与控制台异步:

Public Sub RunPython(file As String, ParamArray args())
  Shell "python.exe """ & file & """ " & Join(args, " ")
End Sub

使用Shell,同步不使用控制台:

Public Function RunPython(file As String, ParamArray args())
  Shell "pythonw.exe """ & file & """ " & Join(args, " ")
End Function

使用WScript.Shell,同步不使用控制台并使用退出代码:

Public Function RunPython(file As String, ParamArray args()) As Long
  Dim obj As Object
  Set obj = CreateObject("WScript.Shell")
  RunPython = obj.Run("pythonw.exe """ & file & """ " & Join(args, " "), 0, True)
End Function

答案 3 :(得分:0)

我有整个Python month on my blog right here。我建立了一个模式,称为网关类,它是一个启用COM的Python类,如果从命令行运行,它将注册自己,并且一旦注册就会用CreateObject(“ foo.bar”)实例化。

这是VBA调用使用某些scipy函数的Python类的一个很好的例子

import numpy as np
import pandas as pd
from scipy.stats import skewnorm


class PythonSkewedNormal(object):
    _reg_clsid_ = "{1583241D-27EA-4A01-ACFB-4905810F6B98}"
    _reg_progid_ = 'SciPyInVBA.PythonSkewedNormal'
    _public_methods_ = ['GeneratePopulation', 'BinnedSkewedNormal']

    def GeneratePopulation(self, a, sz):
        # https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.seed.html
        np.random.seed(10)
        # https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.skewnorm.html
        return skewnorm.rvs(a, size=sz).tolist()

    def BinnedSkewedNormal(self, a, sz, bins):
        # https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.seed.html
        np.random.seed(10)
        # https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.skewnorm.html
        pop = skewnorm.rvs(a, size=sz)
        bins2 = np.array(bins)
        bins3 = pd.cut(pop, bins2)

        table = pd.value_counts(bins3, sort=False)

        table.index = table.index.astype(str)

        return table.reset_index().values.tolist()

if __name__ == '__main__':
    print("Registering COM server...")
    import win32com.server.register
    win32com.server.register.UseCommandLine(PythonSkewedNormal)

和调用VBA代码

Option Explicit

Sub TestPythonSkewedNormal()

    Dim skewedNormal As Object
    Set skewedNormal = CreateObject("SciPyInVBA.PythonSkewedNormal")

    Dim lSize As Long
    lSize = 100

    Dim shtData As Excel.Worksheet
    Set shtData = ThisWorkbook.Worksheets.Item("Sheet3") '<--- change sheet to your circumstances
    shtData.Cells.Clear

    Dim vBins
    vBins = Array(-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5)

    'Stop
    Dim vBinnedData
    vBinnedData = skewedNormal.BinnedSkewedNormal(-5, lSize, vBins)

    Dim rngData As Excel.Range
    Set rngData = shtData.Cells(2, 1).Resize(UBound(vBins) - LBound(vBins), 2)

    rngData.Value2 = vBinnedData

    'Stop

End Sub

完整的评论可以在原始的blog entry here

中找到

这里的优点是没有脱壳。当代码返回时,您就知道它已经完成了,一次脱壳必须检查脱壳过程是否已结束等。此网关类要好得多,恕我直言。