18
11

More than 1 year has passed since last update.

pythonのfunctoolsについてまとめてみた

Posted at

今回のお題

今回は、pythonのfunctoolsというモジュールについてまとめます。

元々はfunctools.wrapについて知りたかっただけなのですが、その過程で他の関数についてもいくつか勉強せざるを得なかったので記念に残しておきます。

目次

  • functoolsモジュールとは
  • functools.partial
  • functools.update_wrapper
  • functools.wrap

functoolsモジュールとは

functoolsモジュールとは、「ある関数を操作したり、またそれによって新たな関数を取得したりできる関数をまとめたモジュール」のことです。

つまり、このモジュールをimportすることで関数に対してさまざまな操作を加えることができるようになるのですね。

言葉だけで説明してもわかりづらいと思うので、早速具体例を紹介していきます。

なお、今後の本記事のソースコードにおいては全てfunctoolsモジュールのimportは省略されています。
その点にはご注意ください。

functools.partial

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には他にもたくさんの関数がありますので、機会があればそちらについてもまとめたいと思います。

18
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
11