元理系院生の新入社員がPythonとJavaで色々頑張るブログ

プログラミングや機械学習について調べた事を書いていきます

Pythonの可変長引数で困った事

この記事に書かれていること

可変長引数にlistを展開して渡す方法

可変長引数呼び出しとは

関数の引数の個数を呼び出し元で自由に指定できる引数呼び出しです。
コマンドライン引数を受け取る時などによく利用されています。
例)
Python

def func(*args, **kargs):
    pass

可変長引数を使っていて困った事

この可変長引数は非常に便利なのですが、次の様なコーディングをした時に困ってしまいました。

def func(*arrays):
    return [f(array) for array in arrays]

このコードは、func関数の呼び出し元でリストを幾つか渡すことで、それぞれのリストに対しf関数を実行するコードです。
このコード自体には特に深い意味はありません。同様の処理を行う場合には便利なmap関数が有ります

このfunc関数は次のように使う事が出来ます。

A = [1,2,3]
B = [4,5,6]
C = [7,8,9]
func(A, B, C) 

func関数の引数は可変長引数である為、Cの後ろにD,E,F...と増やしていく事が可能です。

しかし、次の様なケースでは少し困ったことになってしまいます。

def get_arrays():
    A = [1,2,3]
    B = [4,5,6]
    C = [7,8,9]
    return [A, B, C]

X = get_arrays()
#func(X)

先ほどの例では、func関数に与える変数(A,B,C...)を別々に宣言していた為、可変長引数にそのまま与えることが出来ましたが、今回のケースではA,B,Cが1つのリストにまとまっています。

このままXをfunc関数に与えても、ひとまとまりになったA,B,Cを1つの変数として処理してしまう為、思うように実行されません。

複数の引数ががひとまとまりになってるなら、関数に可変長引数を使わなければ良いのですが、関数定義と引数生成(この場合get_arrays関数)が別々のコーダにコーディングされている場合、リファクタリングは困難です

解決策

可変長引数に渡す為にはひとまとまりになっているXを展開する必要があります。

これが中々厄介で、普遍的な方法で展開してもfunc関数に可変長引数として渡すことが困難です。

しかし、そこは天下のpython。シーケンス型のデータを展開するオペレータが定義されています。

シーケンス型の変数の頭に*を付けると、データが展開されます。

print(*[1,2,3])
print(*[[1,2,3],[4,5,6]])
print(*(1,2,3))
print(*((1,2,3),(4,5,6)))
print(*{"a":1, "b":2})
print(*"abcdefg")

実行結果

1 2 3
[1, 2, 3] [4, 5, 6]
1 2 3
(1, 2, 3) (4, 5, 6)
a b
a b c d e f g

次の様にコーディングすると問題が解決します。

X = get_arrays()
#X = [A, B, C]

func(*X)
#func(A, B, C)