9.8. functools — 高次関数と呼び出し可能オブジェクトの操作

バージョン 2.5 で追加.

ソースコード: Lib/functools.py


functools モジュールは高次関数、つまり関数に影響を及ぼしたり他の関数を返したりする関数、のためのものです。一般に、どんな呼び出し可能オブジェクトでもこのモジュールの目的には関数として扱えます。

モジュール functools は以下の関数を定義します:

functools.cmp_to_key(func)

古いスタイルの比較関数を key function に変換します。 key 関数を受け取る関数 (sorted(), min(), max(), heapq.nlargest(), heapq.nsmallest(), itertools.groupby() など) と共に使用します。この関数は、主に比較関数をサポートしていない Python 3 への移行のためのツールとして用意されています。

比較関数は2つの引数を受け取り、それらを比較し、 “より小さい” 場合は負の数を、同値の場合には 0 を、 “より大きい” 場合には正の数を返す、あらゆる呼び出し可能オブジェクトです。key 関数は呼び出し可能オブジェクトで、1つの引数を受け取り、ソートキーとして使われる値を返します。

例:

sorted(iterable, key=cmp_to_key(locale.strcoll))  # locale-aware sort order

ソートの例と簡単なチュートリアルは ソート HOW TO を参照して下さい。

バージョン 2.7 で追加.

functools.total_ordering(cls)

ひとつ以上の拡張順序比較メソッド (rich comparison ordering methods) を定義したクラスを受け取り、残りを実装するクラスデコレータです。このデコレータは全ての拡張順序比較演算をサポートするための労力を軽減します:

引数のクラスは、 __lt__(), __le__(), __gt__(), __ge__() の中からどれか1つと、 __eq__() メソッドを定義する必要があります。

例えば:

@total_ordering
class Student:
    def __eq__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))

バージョン 2.7 で追加.

functools.reduce(function, iterable[, initializer])

これは reduce() 関数と同じものです。このモジュールからも使えるようにしたのは Python 3 と前方互換なコードを書けるようにするためです。

バージョン 2.6 で追加.

functools.partial(func[,*args][, **keywords])

新しい partial オブジェクトを返します。このオブジェクトは呼び出されると位置引数 args とキーワード引数 keywords 付きで呼び出された func のように振る舞います。呼び出しに際してさらなる引数が渡された場合、それらは args に付け加えられます。追加のキーワード引数が渡された場合には、それらで keywords を拡張または上書きします。大雑把にいうと、次のコードと等価です:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

関数 partial() は、関数の位置引数・キーワード引数の一部を「凍結」した部分適用として使われ、簡素化された引数形式をもった新たなオブジェクトを作り出します。例えば、 partial() を使って base 引数のデフォルトが 2 である int() 関数のように振る舞う呼び出し可能オブジェクトを作ることができます:

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18
functools.update_wrapper(wrapper, wrapped[, assigned][, updated])

wrapper 関数を wrapped 関数に見えるようにアップデートします。オプション引数はタプルで、元の関数のどの属性がラッパ関数の対応する属性に直接代入されるか、またラッパ関数のどの属性が元の関数の対応する属性でアップデートされる(updated)か、を指定します。これらの引数のデフォルト値はモジュールレベル定数 WRAPPER_ASSIGNMENTS (ラッパ関数の __name____module__ 、そしてドキュメンテーション文字列 __doc__ に代入します) と WRAPPER_UPDATES (ラッパ関数の __dict__ 、すなわちインスタンス辞書をアップデートします) です。

この関数は主に関数を包んでラッパを返すデコレータ (decorator) 関数の中で使われるよう意図されています。もしラッパ関数がアップデートされないとすると、返される関数のメタデータは元の関数の定義ではなくラッパ関数の定義を反映してしまい、これは通常あまり有益ではありません。

functools.wraps(wrapped[, assigned][, updated])

これはラッパ関数を定義するときに update_wrapper() を関数デコレータとして呼び出す便宜関数です。これは partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) と等価です。例えば:

>>> from functools import wraps
>>> def my_decorator(f):
...     @wraps(f)
...     def wrapper(*args, **kwds):
...         print 'Calling decorated function'
...         return f(*args, **kwds)
...     return wrapper
...
>>> @my_decorator
... def example():
...     """Docstring"""
...     print 'Called example function'
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'

このデコレータ・ファクトリーを使わなければ、上の例中の関数の名前は 'wrapper' となり、元々の example() のドキュメンテーション文字列は失われたところです。

9.8.1. partial オブジェクト

partial オブジェクトは、 partial() 関数によって作られる呼び出し可能オブジェクトです。オブジェクトには読み取り専用の属性が三つあります:

partial.func

呼び出し可能オブジェクトまたは関数です。 partial オブジェクトの呼び出しは新しい引数とキーワードと共に func に転送されます。

partial.args

最左の位置引数で、 partial オブジェクトの呼び出し時にその呼び出しの際の位置引数の前に追加されます。

partial.keywords

partial オブジェクトの呼び出し時に渡されるキーワード引数です。

partial オブジェクトは function オブジェクトのように呼び出し可能で、弱参照可能で、属性を持つことができます。重要な相違点もあります。例えば、 __name____doc__ 両属性は自動では作られません。また、クラス中で定義された partial オブジェクトはスタティックメソッドのように振る舞い、インスタンスの属性問い合わせの中で束縛メソッドに変換されません。