python - script - pyxll
¿Hay alguna manera de llamar a un código Python en Excel-VBA? (4)
Hay varias formas de ejecutar un script de Python con VBA, dependiendo de si es necesario esperar al final de la ejecución y saber si se realizó sin errores.
Con Shell , asíncrono con consola:
Public Sub RunPython(file As String, ParamArray args())
Shell "python.exe """ & file & """ " & Join(args, " ")
End Sub
Con Shell , síncrono sin consola:
Public Function RunPython(file As String, ParamArray args())
Shell "pythonw.exe """ & file & """ " & Join(args, " ")
End Function
Con WScript.Shell , síncrono sin consola y con código de salida:
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
Tengo un archivo de Excel (Main.xlsm) que contiene macros. Tengo un archivo de Python (python.py) para generar un archivo de Excel subsidiario (sub.xlsx) que llamaría en las macros del archivo Main.xlsm Este archivo sub.xlsx generado por la ejecución de python.py se guarda en el mismo directorio de trabajo.
Ahora quiero hacer que este python.py se ejecute durante la ejecución de las macros Main.xlsm y luego use este archivo xlsx. Básicamente quiero reducir el paso de ejecutar python.py externamente. ¿Hay un comando para eso? Soy nuevo en VBA.
Sí hay. Mi forma preferida de hacer esto es a través de xlwings ( https://www.xlwings.org/ ), pero también hay varias otras opciones. XlWings es genial porque es gratuito, de código abierto y fácil de usar, con excelente documentación. Sin embargo, hay algunas limitaciones de funciones, por lo que tendría que verificar si se ajusta a sus necesidades.
Tuve un mes completo de Python en mi blog aquí . Establezco un patrón al que llamo la clase de puerta de enlace que es una clase Python habilitada para COM, se registrará si se ejecuta desde la línea de comandos y una vez registrada se crea una instancia con CreateObject ("foo.bar").
Aquí hay un buen ejemplo de VBA llamando a una clase de Python que usa algunas funciones de scipy
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)
y el código VBA de llamada
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
El comentario completo se puede encontrar en la entrada del blog original aquí
La ventaja aquí es que no hay bombardeos. Cuando el código que devuelve, usted sabe que ha terminado, con el bombardeo una vez tiene que verificar si el proceso de shell ha finalizado, etc. Esta clase de puerta de enlace es mucho mejor que la de mi trabajo.
La forma más sencilla es ejecutar el intérprete de Python con el comando Shell
Shell ("python.exe " & yourScript & " " & arguments)