今回のお題
今回は、pythonのfunctoolsというモジュールについてまとめます。
元々はfunctools.wrapについて知りたかっただけなのですが、その過程で他の関数についてもいくつか勉強せざるを得なかったので記念に残しておきます。
目次
- functoolsモジュールとは
- functools.partial
- functools.update_wrapper
- functools.wrap
functoolsモジュールとは
functoolsモジュールとは、「ある関数を操作したり、またそれによって新たな関数を取得したりできる関数をまとめたモジュール」のことです。
つまり、このモジュールをimportすることで関数に対してさまざまな操作を加えることができるようになるのですね。
言葉だけで説明してもわかりづらいと思うので、早速具体例を紹介していきます。
なお、今後の本記事のソースコードにおいては全てfunctoolsモジュールのimportは省略されています。
その点にはご注意ください。
functools.partial
functools.partialは、ある関数の引数やキーワード引数の一部を固定して新しい関数を作り出すことができます。
def info(name, age):
print(f"{name}さんは{age}歳です。")
func_1 = functools.partial(info, "John")
func_1(30)
# 出力:Johnさんは30歳です。
上記の例では、partialの第一引数であるinfo関数に対してpartialの第二引数である"John"が渡されています。
これによりinfo関数の第一引数がJohnで固定された新しい関数が作られ、それがfunc_1に代入されています。
なお、上記の例でinfoの第一引数以外(例えばage)に引数を渡したい場合には、キーワード引数を用いることになります。
def info(name, age):
print(f"{name}さんは{age}歳です。")
func_1 = functools.partial(info, age=30)
func_1("John")
# 出力 Johnさんは30歳です。
functools.update_wrapper
functools.update_wrapperは、「ある関数の属性を別の関数の属性で上書きする」ためのものです。
属性とは関数名や関数のモジュール名などのことで、クラスやモジュールなどにも属性が設定されています。
print(print.__name__)
# 出力:print
# printという関数の関数名はprint
print(print.__module__)
# 出力:builtins
# printはビルトイン関数
class Hoge:
pass
print(Hoge.__name__)
# 出力:Hoge
# Hogeクラスのクラス名はHoge
では、update_wrapperで属性値を上書きするとどうなるでしょうか。
def hoge():
pass
def fuga():
pass
# そのまま出力
print(hoge.__name__) # 出力:hoge
# 上書きして出力
print(update_wrapper(hoge, fuga).__name__) # 出力:fuga
ちなみに、上書きされる要素は以下の通りであり、逆にこの部分を引数で指定することでアップデート内容を操作できます。
WRAPPER_ASSIGNMENTS(第3引数) = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')
WRAPPER_UPDATES(第4引数) = ('__dict__',)
def hoge():
pass
def fuga():
pass
# そのまま出力
print(hoge.__name__) # 出力:hoge
# 引数を限定した上書き
print(update_wrapper(hoge, fuga, "__doc__").__name__) # 出力:hoge
また、update_wrapperで上書きした内容は基本的にその場限りではなく持続します。
def hoge():
pass
def fuga():
pass
# そのまま出力
print(hoge.__name__) # ここでは出力:hoge
# 上書き
print(update_wrapper(hoge, fuga).__name__) # ここで出力:fugaに上書き
print(hoge.__name__) # ここでも出力:fugaのまま
なお、update_wrapperを用いた上書きは、主にで主にデコレータによる属性値の上書きを防ぐ目的で使用されるようです。
デコレータ、及びデコレータによる属性値の上書きについては今回は省略するので、各自でお調べいただくようにお願いいたします。
functools.wraps
この関数については、上記のupdate_wrapperを踏まえた上で公式ドキュメント(の日本語訳)を引用します。
これはラッパー関数を定義するときに update_wrapper() を関数デコレータとして呼び出す便宜関数です。
つまりつまりあるデコレータを定義する際にこのfunctools.wrapsをデコレータとして用いておくことで、デコレータによる属性値の上書きを防ぐことができるのですね。
import functools
def deco(f):
@functools.wraps(f, "__name__")
def wrapper(*args, **kwargs):
"""test"""
return f(*args, **kwargs)
return wrapper
@deco
def hoge():
"""sample"""
pass
print(hoge.__name__)
# 出力:hoge
# __name__はwrapsで指定しているのでそのまま
print(hoge.__doc__)
# 出力:test
# __doc__はwrapsで指定していないので上書きされる
今回は例として属性値を指定する方法を取りましたが、指定しなかった場合の初期値はupdate_wrapperと同じです。
また、公式にはデコレータ定義時に使用、とありましたが、属性値を上書きするデコレータとしても一応使えます。
import functools
@functools.wraps(sum)
def hoge():
pass
print(hoge.__name__) # 出力:sum
終わりに
以上でfunctoolsについての解説を終わります。
今回はfunctools.wrapsを理解する上で必要になった箇所をまとめただけですが、functoolsには他にもたくさんの関数がありますので、機会があればそちらについてもまとめたいと思います。