pythonの関数で使うpack,unpack機能
pythonで関数を作っていると*argsや**kwargsという標記が出てきます。その都度、意味を確認してもすぐにわからなくなり、出くわすたびに引いてしまいます。しかし、matplotlibをいろいろな使ってグラフを描くうえで、プログラムを見やすくするために、描画部分を関数化したいとの衝動にかられます。この際、引数の渡し方が複雑になるので、*argsや**kwargsの使用を考えなければなりません。そこで、関数への引数の渡し方を整理してみます。
関数側 parameter(仮引数) main側argument(実引数)
関数を使って処理をするときには、関数を呼ぶプログラムは関数に対し処理内容を指示するため引数を渡します。引数は呼ばれる関数と呼ぶ側のmainのプログラムとでは呼び名を区別していて、前者をparameter(仮引数)、後者をargument(実引数)といいます。具体的には、次の通りmainのプログラムから関数にargument:実引数を渡し、関数側はparameter:仮引数を受けて処理を実行します。
def func(parameter): # parameter:仮引数
print(parameter)
argument = 'text'
func(argument) # argument:実引数
可変長引数variable length arguments
ここで、渡された複数の引数の合計を計算するような関数で、その引数の数が変動するような場合にを考えると、*argsを使うと効果的です。そこで、渡した引数を2乗した値を合計する関数を考えます。
def func_pack_arg(*arg):
print(arg)
total = 0
for num in arg:
total += num**2
return total
print(func_pack_arg(1, 2, 3, 4, 5))
# (1, 2, 3, 4, 5)
# 55
上記の通り、関数側のparameter(仮引数)に*を付けて、呼び出す方のmainで複数のargument(実引数)をカンマで区切って呼び出すと、関数側にタプルにまとめて渡されます。包む(パック)するという意味でpackingという表現を使います。このため、関数の中では受け取った複数の引数をタプルとして渡されるので、for文などを使い、引数の数に応じた処理をすることができます。
parameterだからpackと覚えておくとよいと思います。このような引数の渡し方を可変長引数(variable length arguments)といいます。
リストを引数とすることの問題
mainのプログラムでリストを作成しており、このリストを引数として、関数を呼び出すようなケースもあります。このときには、次のように可変長引数の関数を使わず、リストを引数として渡してしまう方法も考えられますが、これには少し問題があります。
def func_list_arg(arg):
print(arg)
total = 0
for num in arg:
total += num**2
arg[2]=100
return total
parm_list=[1, 2, 3, 4, 5]
print(func_list_arg(parm_list))
print(parm_list)
# [1, 2, 3, 4, 5]
# 55
# [1, 2, 100, 4, 5]
リストparm_listを引数として渡すことにより計算は正しくできますが、6行目で関数の中で引数のリストを変更すると、mainで作成したリストも連動して変わってしまうということです。
リストやタプルのunpack
*argsについては、unpackという機能もあります。 次のようにrange関数の引数として(3 ,6)とすると、3から5(6の1つ前)まで範囲ということを示し、これにlist関数を適用すると[3, 4, 5]というリストを作成します。この、(3, 6)をタプルとして*argsに代入し、*:アスタリスクを付けてrange関数を適用すると、タプルの()が取れて、'3,6'がrange関数の引数になります。
print(range(3,6))
print(list(range(3, 6)))
args = (3, 6)
print(range(*args))
print(list(range(*args)) )
# range(3, 6)
# [3, 4, 5]
# range(3, 6)
# [3, 4, 5]
この方法はリストについても使うことができます。
args = [3, 6]
print(range(*args))
print(list(range(*args)))
# range(3, 6)
# [3, 4, 5]
このようにタプルやリストの名前の頭に*を付けると()や[]を取ったものに変換してくれます。()や[]をパックに見立ててunpackと呼んでいます。
自作関数の引数のunpack
同じように、自分で作った関数についても、引数にアンパックを使うことができます。この際、値はタプルでもリストでも大丈夫です。
def func_unpack_arg(x, y):
return x, y
t1=('xt', 'yt')
print(func_unpack_arg(*t1))
l1=['xl', 'yl']
print(func_unpack_arg(*l1))
# ('xt', 'yt')
# ('xl', 'yl')
上記のようなunpackは、あまり使い道が思いつきませんが、2次元の配列の縦と横を置き換える(転置行列)を作るようなものが有名です。
unpackを使った転置行列の計算
matrix = [
[ 0, 1, 2, 3],
[10, 11, 12, 13],
[20, 21, 22, 23]
]
print(list(zip(*matrix)))
print(list(map(list, zip(*matrix))))
# [(0, 10, 20), (1, 11, 21), (2, 12, 22), (3, 13, 23)]
# [[0, 10, 20], [1, 11, 21], [2, 12, 22], [3, 13, 23]]
2次元配列に対し、unpackを適用すると、外側の()括弧がはずれ、3つのリストが取り出されます。これにzip関数を適用すると3つのリストからそれぞれ、1つずつ要素が取り出されるので、これをリストに変換します。このことにより元の配列の行列を入れ替えた転置行列を作成することができます。