gpt4 book ai didi

python - 将 numpy 数组从 VBA 移动到 Python 并返回

转载 作者:太空狗 更新时间:2023-10-29 21:00:23 25 4
gpt4 key购买 nike

我在 Microsoft Access 中有一个 VBA 脚本。 VBA 脚本是多人参与的大型项目的一部分,因此无法离开 VBA 环境。

在我的脚本的一部分中,我需要在表格上快速完成复杂的线性代数运算。因此,我将 VBA 表移动为 recordsets ) 转换成 Python 来做线性代数,然后再转换成 VBA。 python 中的矩阵表示为 numpy 数组。

一些线性代数是专有的,因此我们正在使用 pyinstaller 编译专有脚本.

具体过程如下:

  1. VBA 脚本创建一个代表表格 input.csv 的 csv 文件。
  2. VBA脚本通过命令行运行python脚本
  3. python 脚本将 csv 文件 input.csv 加载为 numpy 矩阵,对其执行线性代数,并创建输出 csv 文件 output.csv
  4. VBA 等待 python 完成,然后加载 output.csv
  5. VBA 删除不再需要的 input.csv 文件和 output.csv 文件。

这个过程效率低下。

有没有一种方法可以将 VBA 矩阵加载到 Python 中(并返回)而不会造成 csv 困惑?这些方法是否适用于通过 pyinstaller 编译的 python 代码?

我在 stackoverflow 上找到了以下相关示例。但是,他们没有专门解决我的问题。

Return result from Python to Vba

How to pass Variable from Python to VBA Sub

最佳答案

解决方案1

要么检索 Access 的 COM 运行实例,要么通过 COM API 使用 python 脚本直接获取/设置数据:

VBA:

Private Cache

Public Function GetData()
GetData = Cache
Cache = Empty
End Function

Public Sub SetData(data)
Cache = data
End Sub

Sub Usage()
Dim wshell
Set wshell = VBA.CreateObject("WScript.Shell")

' Make the data available via GetData()'
Cache = Array(4, 6, 8, 9)

' Launch the python script compiled with pylauncher '
Debug.Assert 0 = wshell.Run("C:\dev\myapp.exe", 0, True)

' Handle the returned data '
Debug.Assert Cache(3) = 2
End Sub

Python(myapp.exe):

import win32com.client

if __name__ == "__main__":

# get the running instance of Access
app = win32com.client.GetObject(Class="Access.Application")

# get some data from Access
data = app.run("GetData")

# return some data to Access
app.run("SetData", [1, 2, 3, 4])

解决方案2

或者创建一个 COM 服务器来向 Access 公开一些功能:

VBA:

Sub Usage()
Dim Py As Object
Set Py = CreateObject("Python.MyModule")

Dim result
result = Py.MyFunction(Array(5, 6, 7, 8))
End Sub

Python(myserver.exemyserver.py):

import sys, os, win32api, win32com.server.localserver, win32com.server.register

class MyModule(object):

_reg_clsid_ = "{5B4A4174-EE23-4B70-99F9-E57958CFE3DF}"
_reg_desc_ = "My Python COM Server"
_reg_progid_ = "Python.MyModule"
_public_methods_ = ['MyFunction']

def MyFunction(self, data) :
return [(1,2), (3, 4)]


def register(*classes) :
regsz = lambda key, val: win32api.RegSetValue(-2147483647, key, 1, val)
isPy = not sys.argv[0].lower().endswith('.exe')
python_path = isPy and win32com.server.register._find_localserver_exe(1)
server_path = isPy and win32com.server.register._find_localserver_module()

for cls in classes :
if isPy :
file_path = sys.modules[cls.__module__].__file__
class_name = '%s.%s' % (os.path.splitext(os.path.basename(file_path))[0], cls.__name__)
command = '"%s" "%s" %s' % (python_path, server_path, cls._reg_clsid_)
else :
file_path = sys.argv[0]
class_name = '%s.%s' % (cls.__module__, cls.__name__)
command = '"%s" %s' % (file_path, cls._reg_clsid_)

regsz("SOFTWARE\\Classes\\" + cls._reg_progid_ + '\\CLSID', cls._reg_clsid_)
regsz("SOFTWARE\\Classes\\AppID\\" + cls._reg_clsid_, cls._reg_progid_)
regsz("SOFTWARE\\Classes\\CLSID\\" + cls._reg_clsid_, cls._reg_desc_)
regsz("SOFTWARE\\Classes\\CLSID\\" + cls._reg_clsid_ + '\\LocalServer32', command)
regsz("SOFTWARE\\Classes\\CLSID\\" + cls._reg_clsid_ + '\\ProgID', cls._reg_progid_)
regsz("SOFTWARE\\Classes\\CLSID\\" + cls._reg_clsid_ + '\\PythonCOM', class_name)
regsz("SOFTWARE\\Classes\\CLSID\\" + cls._reg_clsid_ + '\\PythonCOMPath', os.path.dirname(file_path))
regsz("SOFTWARE\\Classes\\CLSID\\" + cls._reg_clsid_ + '\\Debugging', "0")

print('Registered ' + cls._reg_progid_)


if __name__ == "__main__":
if len(sys.argv) > 1 :
win32com.server.localserver.serve(set([v for v in sys.argv if v[0] == '{']))
else :
register(MyModule)

请注意,您必须在不带任何参数的情况下运行脚本一次以注册该类并使其可用于 VBA.CreateObject

这两种解决方案都适用于 pylauncher,并且可以使用 numpy.array(data) 转换在 python 中接收的数组。

依赖:

https://pypi.python.org/pypi/pywin32

关于python - 将 numpy 数组从 VBA 移动到 Python 并返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45105388/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com