PR

【xlwings】VBAからPythonへの引数/戻り値の挙動まとめ

xlwingsでVBAからPythonの関数に配列を引数として渡したりnumpy配列を戻り値として受け取るときの挙動をまとめる

スポンサーリンク

VBAからPythonへの引数の渡し方

xlwingsを使ってVBAのコード内でpython内の関数と引数や戻り値をやり取りするためには,ユーザー定義関数(UDF)を使う必要がある

関数の引数および戻り値
技術的には、 RunPython が呼び出す関数に引数を含めることは可能ですが、使い勝手は悪いです。また、 RunPython には戻り値がありません。これらを解決するには、UDFを使いましょう。詳細については ユーザー定義関数 (UDFs) をご覧ください。ただし、UDFを利用できるのは現在Windowsのみとなっています。

RunPython - xlwings Documentation
ユーザー定義関数 (UDFs) - xlwings Documentation

というわけでいろいろな組み合わせを試してみる

単一の引数を渡す/戻り値を受け取る

次のようなマクロ有効ブック(.xlsm)を用意する

Pythonのテストプログラム(test.py)

#!python3.6
import os

import numpy as np
import xlwings as xw

@xw.func
def py_type(arr):
    msg = str(type(arr))
    return msg

@xw.func
def np_shape(arr):
    msg = str(np.array(arr).shape)
    return msg

@xw.func
def np_arr(arr):
    np_arr = type(arr)
    return np_arr
    
if __name__ == "__main__":
    wb = xw.Book("myproject.xlsm")
    macro = wb.macro('SampleCall')
    macro()

VBAのサンプルプログラム

Sub SampleCall()

Dim arr1 As Variant
Dim arr2 As Variant
Dim msg1 As Variant
Dim msg2 As Variant
Dim arr_shape As Variant

arr1 = Range("A1:A10")

msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)

MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape
    
End Sub

引数はVariant型で渡し,戻り値もVariant型で受け取る

それではいってみよう

1行1列のセル

1行1列のセルを読み込んでVBAからPythonに引数として渡すと,Python側ではfloat型の変数になる

numpy配列には変換できない

float型の変数を戻り値としてVBAで受け取ると,VBA側ではDouble型の変数になる

UDF
マクロ
'マクロ
arr1 = Range("A1")

msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)

MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape

実行結果

n行1列のセル

n行1列のセルを読み込んでVBAからPythonに引数として渡すと,Python側ではlist型の変数になり,numpy配列に変換すると大きさは(n,)になる

大きさ(n,)のnumpy配列を戻り値としてVBAで受け取ると,VBA側では(n,)の1次元配列になる

UDF
マクロ
'マクロ
arr1 = Range("A1:A10")

msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)

MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape

実行結果

1行n列のセル

1行n列のセルを読み込んでVBAからPythonに引数として渡すと,Python側ではlist型の変数になり,numpy配列に変換すると大きさは(n,)になる

大きさ(n,)のnumpy配列を戻り値としてVBAで受け取ると,VBA側では(n,)の1次元配列になる

UDF
マクロ
'マクロ
arr1 = Range("A1:A10")

msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)

MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape

実行結果

m行n列のセル

m行n列のセルを読み込んでVBAからPythonに引数として渡すと,Python側ではlist型の変数になり,numpy配列に変換すると大きさは(m,n)になる

大きさ(m,n)のnumpy配列を戻り値としてVBAで受け取ると,VBA側では(m,n)の2次元配列になる

UDF
マクロ
'マクロ
arr1 = Range("A1:C10")

msg1 = xlwings_udfs.py_type(arr1)
msg2 = xlwings_udfs.np_shape(arr1)
arr2 = xlwings_udfs.np_arr(arr1)
arr_shape = arr_size(arr2)

MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape

実行結果

複数の引数を渡す/戻り値をもらう

複数の引数を渡す場合はカンマで区切る

複数の戻り値をもらう場合は1つの配列で受け取る

複数の配列を戻り値に指定した場合は,戻り値に指定した配列をそれぞれ要素に持つ配列(多次元配列)になる

UDF
#!python3.6
import os

import numpy as np
import xlwings as xw

@xw.func
def py_types(arr1,arr2):
    msg = str(type(arr1))+str(type(arr2))
    return msg

@xw.func
def np_shapes(arr1,arr2):
    msg = str(np.array(arr1).shape)+str(np.array(arr2).shape)
    return msg

@xw.func
def np_arrs(arr1,arr2):
    np_arr1 = np.array(arr1)
    np_arr2 = np.array(arr2)
    return np_arr1, np_arr2

if __name__ == "__main__":
    wb = xw.Book("myproject.xlsm")
    macro = wb.macro('SampleCall')
    macro()
マクロ
Sub SampleCall()

Dim arr1 As Variant
Dim arr2 As Variant
Dim arr3 As Variant
Dim msg1 As Variant
Dim msg2 As Variant
Dim arr_shape As Variant

arr1 = Range("A1:C10")
arr2 = Range("A1:G5")

msg1 = xlwings_udfs.py_types(arr1, arr2)
msg2 = xlwings_udfs.np_shape(arr1, arr2)
arr3 = xlwings_udfs.np_arr(arr1, arr2)
arr_shape = arr_size(arr3)

MsgBox msg1 & vbCrLf & msg2 & vbCrLf & arr_shape
    
End Sub

実行結果

ローカルウィンドウを見てみるとこんな感じ

まとめ

xlwingsでVBAからPythonの関数に引数を渡すときとnumpy配列を戻り値として返すときの挙動をまとめると次のようになる

引数として与える配列Pythonでの型numpyに変換したときの配列戻り値として受け取る配列
(1,1)float-Double
(n,1)list(n,)(n,)
(1,n)list(n,)(n,)
(m,n)list(m,n)(m,n)
(m1,n1), ..., (mi,ni)list, ..., list(m1,n1), ..., (mi,ni) ((m1,n1), ..., (mi,ni))

VBAのVariant型はPythonのlistと同じくらい融通が利く

↓Pythonのインストールについてはこちら

コメント