部分適用とは、一部引数の値を固定にし、別の関数として扱うことです。
リトルエンディアンで2バイト、4バイト、2バイトの整数が格納されているバイト列を考えます。
>>> import binascii >>> b = binascii.unhexlify('0100020000000300')
このバイト列から2バイト、4バイト、2バイトの順で3つの整数を取り出すソースコードは以下のようになります。
>>> i1 = int.from_bytes(b[0:2], 'little') >>> i2 = int.from_bytes(b[2:6], 'little') >>> i3 = int.from_bytes(b[6:8], 'little') >>> i1, i2, i3 (1, 2, 3)
上のソースコードでは、int.from_bytes() を呼ぶ度に 引数'little' を指定しているのが、あまり格好良い感じではないですね。省略できればなぁ~、と思えてきます。
ここで、functools.partial() を使って引数の部分適用をすると、ソースコードが一気に見やすくなります。
以下は functools.partial() の具体的な使用例です。
>>> from functools import partial >>> to_int = partial(int.from_bytes, byteorder='little') >>> i1 = to_int(b[0:2]) >>> i2 = to_int(b[2:6]) >>> i3 = to_int(b[6:8]) >>> i1, i2, i3 (1, 2, 3)バイト列から整数への変換部分がスッキリしましたね。
上例は functools.partial() の便利さを示すものですが、functools.partial() のより大きな恩恵として「部分適用した関数を変数に代入可能」が挙げられます。
つまり、、、
>>> def other_func(to_int): ... print(to_int(b[0:2])) ... print(to_int(b[2:6])) ... print(to_int(b[6:8])) ... # partialの戻り値は変数に代入可能 >>> other_func(partial(int.from_bytes, byteorder='little')) 1 2 3 # partial無しでは変数に代入できない >>> other_func(int.from_bytes(byteorder='little')) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Required argument 'bytes' (pos 1) not found上記のように、functools.partial() ならば部分適用した関数を変数(ここでは引数)へ代入可能です。
functools.partial() 無しで実現するには、別途関数を定義する、lambdaを使う、等の方法がありますが、簡潔さと可読性の両観点から、部分適用には functools.partial() を使うのが最適であると言えます。
0 件のコメント:
コメントを投稿