Source

pymotw-ja / PyMOTW / functools / index.rst

Full commit

functools -- デコレータを作るためのツールとその他の関数ラッパ

目的:functools モジュールは関数やその他の呼び出し可能オブジェクトをラップするためのツールを提供します
Python バージョン:2.5 で新規追加

functools モジュールで提供される主なツールは、デフォルト引数で呼び出し可能オブジェクトを "ラップ" するために使用される partial クラスです。その結果として返されるオブジェクトはそれ自身が呼び出し可能でオリジナルの関数を経由するように扱われます。オリジナルの呼び出し可能オブジェクトと同じ全ての引数を取り、さらに追加の位置引数やキーワード引数を受け取って実行することができます。

partial

このサンプルは myfunc() 関数のために2つの単純な partial オブジェクトの説明をします。 show_details() は渡された関数、その引数と partial オブジェクトの keywords 属性を表示することに注目してください。

このサンプルの最後で、最初に作成した partial オブジェクトの仮引数 a に値を渡さずに実行するので例外が発生します。

$ python functools_partial.py
myfunc:
        object: <function myfunc at 0x822b0>
        __name__: myfunc
        __doc__ 'Docstring for myfunc().'
        called myfunc with: ('a', 3)

partial with named default:
        object: <functools.partial object at 0x880f0>
        __doc__ 'partial(func, *args, **keywords) - new function with partial application\n\tof the given arguments and keywords.\n'
        func: <function myfunc at 0x822b0>
        args: ()
        keywords: {'b': 4}
        called myfunc with: ('default a', 4)
        called myfunc with: ('override b', 5)

partial with defaults:
        object: <functools.partial object at 0x88120>
        __doc__ 'partial(func, *args, **keywords) - new function with partial application\n\tof the given arguments and keywords.\n'
        func: <function myfunc at 0x822b0>
        args: ('default a',)
        keywords: {'b': 99}
        called myfunc with: ('default a', 99)
        called myfunc with: ('default a', 'override b')

Insufficient arguments:
Traceback (most recent call last):
  File "functools_partial.py", line 49, in <module>
    p1()
TypeError: myfunc() takes at least 1 non-keyword argument (0 given)

update_wrapper

partial オブジェクトはデフォルトで __name____doc__ 属性を持っていません。デコレートされた関数のそういった属性をなくしてしまうことはデバッグをより難しくしてしまいます。 update_wrapper を使用することでオリジナルの関数から partial オブジェクトに渡された属性をコピー又は追加することができます。

そのラッパへ追加される属性は functools.WRAPPER_ASSIGNMENTS で定義されます。一方 functools.WRAPPER_UPDATES は変更される値を表示します。

$ python functools_update_wrapper.py
myfunc:
        object: <function myfunc at 0x82230>
        __name__: myfunc
        __doc__ 'Docstring for myfunc().'

raw wrapper:
        object: <functools.partial object at 0x7bfc0>
        __name__: (no __name__)
        __doc__ 'partial(func, *args, **keywords) - new function with partial application\n\tof the given arguments and keywords.\n'

Updating wrapper:
        assign: ('__module__', '__name__', '__doc__')
        update: ('__dict__',)

updated wrapper:
        object: <functools.partial object at 0x7bfc0>
        __name__: myfunc
        __doc__ 'Docstring for myfunc().'

メソッドとその他の呼び出し可能オブジェクト

partial はメソッドやインスタンスも含め、どのような呼び出し可能オブジェクトでも動作します。

$ python functools_method.py
meth1 straight:
        object: <bound method MyClass.meth1 of <__main__.MyClass object at 0x85b10>>
        __name__: meth1
        __doc__ 'Docstring for meth1().'
        called meth1 with: (<__main__.MyClass object at 0x85b10>, 'no default for a', 3)

meth1 wrapper:
        object: <functools.partial object at 0x88300>
        __name__: meth1
        __doc__ 'Docstring for meth1().'
        called meth1 with: (<__main__.MyClass object at 0x85b10>, 'a goes here', 4)

meth2:
        object: <bound method MyClass.meth2 of <__main__.MyClass object at 0x85b10>>
        __name__: meth2
        __doc__ 'Docstring for meth2'
        called meth2 with: (<__main__.MyClass object at 0x85b10>, 'no default for c', 6)

wrapped meth2:
        object: <functools.partial object at 0x88270>
        __name__: meth2
        __doc__ 'Docstring for meth2'
        called meth2 with: ('wrapped c', 'no default for c', 6)

instance:
        object: <__main__.MyClass object at 0x85b10>
        __name__: (no __name__)
        __doc__ 'Demonstration class for functools'
        called object with: (<__main__.MyClass object at 0x85b10>, 'no default for e', 6)

instance wrapper:
        object: <functools.partial object at 0x88330>
        __name__: (no __name__)
        __doc__ 'partial(func, *args, **keywords) - new function with partial application\n\tof the given arguments and keywords.\n'
        called object with: (<__main__.MyClass object at 0x85b10>, 'e goes here', 7)

wraps

ラップされた呼び出し可能オブジェクトのプロパティ更新は、decorated 関数が最終的にオリジナルの "raw" 関数のプロパティになるのでデコレータで行うとかなり便利です。 :mod:`functools`wraps() という便利な関数を提供します。それはデコレータのように使用されて自動的に update_wrapper() を適用します。

$ python functools_wraps.py
myfunc:
        object: <function myfunc at 0x82330>
        __name__: myfunc
        __doc__ None

        myfunc: ('unwrapped, default b', 2)
        myfunc: ('unwrapped, passing b', 3)

wrapped_myfunc:
        object: <function myfunc at 0x82370>
        __name__: myfunc
        __doc__ None

        decorated: ('decorated defaults', 1)
                myfunc: ('decorated defaults', 1)
        decorated: ('args to decorated', 4)
                myfunc: ('args to decorated', 4)