functools --- 高階関数と呼び出し可能オブジェクトの操作¶
ソースコード: Lib/functools.py
functools モジュールは高階関数、つまり関数に影響を及ぼしたり他の関数を返したりする関数のためのものです。一般に、どんな呼び出し可能オブジェクトでもこのモジュールの目的には関数として扱えます。
モジュール functools は以下の関数を定義します:
- @functools.cache(user_function)¶
簡単で軽量な無制限の関数キャッシュです。 "メモ化 (memoize)" とも呼ばれることがあります。
Returns the same as
lru_cache(maxsize=None), creating a thin wrapper around a dictionary lookup for the function arguments. Because it never needs to evict old values, this is smaller and faster than@lru_cachewith a size limit.例えば:
@cache def factorial(n): return n * factorial(n-1) if n else 1 >>> factorial(10) # no previously cached result, makes 11 recursive calls 3628800 >>> factorial(5) # no new calls, just returns the cached result 120 >>> factorial(12) # two new recursive calls, factorial(10) is cached 479001600
キャッシュはスレッドセーフなので、ラップされた関数を複数のスレッドで使用できます。 これは、同時に更新が行われても、内部のデータ構造の整合性が保たれるという意味です。
最初の呼び出しが完了して結果がキャッシュされるまでに別のスレッドから呼び出された場合、ラップされた関数が複数回呼び出される可能性があります。
Added in version 3.9.
- @functools.cached_property(func)¶
Transform a method of a class into a property whose value is computed once and then cached as a normal attribute for the life of the instance. Similar to
@property, with the addition of caching. Useful for expensive computed properties of instances that are otherwise effectively immutable.以下はプログラム例です:
class DataSet: def __init__(self, sequence_of_numbers): self._data = tuple(sequence_of_numbers) @cached_property def stdev(self): return statistics.stdev(self._data)
The mechanics of
@cached_propertyare somewhat different from@property. A regular property blocks attribute writes unless a setter is defined. In contrast, a cached_property allows writes.cached_property デコレータが実行されるのは、属性が検索され、かつ同名の属性がまだ存在しない場合だけです。 実行されると、cached_property は同名の属性に値を書き込みます。 その後の属性の読み書きは cached_property メソッドより優先されるため、通常の属性と同じように動作します。
キャッシュされた値は属性を削除することで取り除くことができます。これにより cached_property メソッドを再度実行することが可能になります。
cached_property は、複数スレッドで使用する際に起こり得る競合状態を防ぎません。 同じインスタンスに対してゲッター関数が複数回実行され、最後の実行がキャッシュの値を設定する場合があります。 キャッシュされるプロパティが冪等であるなど、同じインスタンスで複数回実行しても害がなければ問題ありません。 同期が必要な場合は、デコレートしたゲッター関数内か、キャッシュされるプロパティにアクセスする箇所の前後で、必要なロックを実装してください。
このデコレータは PEP 412 のキー共有辞書の動作を妨げる点に注意してください。 そのため、インスタンスの辞書が通常より多くのメモリを使うことがあります。
また、このデコレータは各インスタンスの
__dict__属性が可変のマッピングであることを要求します。すなわち、このデコレータはいくつかの型、たとえばメタクラス (型インスタンスの__dict__属性はクラスの名前空間に対する読み込み専用のプロキシであるため) や、__slots__を指定していてその中に__dict__を含まない型 (それ自体が__dict__属性を提供しないため) に対しては動作しないことを意味します。If a mutable mapping is not available or if space-efficient key sharing is desired, an effect similar to
@cached_propertycan also be achieved by stacking@propertyon top of@lru_cache. See メソッド呼び出しをキャッシュするには どうしたらいいですか? for more details on how this differs from@cached_property.Added in version 3.8.
バージョン 3.12 で変更: Prior to Python 3.12,
@cached_propertyincluded an undocumented lock to ensure that in multi-threaded usage the getter function was guaranteed to run only once per instance. However, the lock was per-property, not per-instance, which could result in unacceptably high lock contention. In Python 3.12+ this locking is removed.
- functools.cmp_to_key(func)¶
古いスタイルの比較関数を key function に変換します。key 関数を受け取るツール (
sorted(),min(),max(),heapq.nlargest(),heapq.nsmallest(),itertools.groupby()など) と共に使用します。この関数は、主に比較関数を使っていた Python 2 からプログラムの移行のための変換ツールとして使われます。比較関数は2つの引数を受け取り、それらを比較し、 "より小さい" 場合は負の数を、同値の場合には 0 を、 "より大きい" 場合には正の数を返す、あらゆる呼び出し可能オブジェクトです。key 関数は呼び出し可能オブジェクトで、1つの引数を受け取り、ソートキーとして使われる値を返します。
以下はプログラム例です:
sorted(iterable, key=cmp_to_key(locale.strcoll)) # ロケールに応じた順序でソート
ソートの例と簡単なチュートリアルは ソートのテクニック を参照して下さい。
Added in version 3.2.
- @functools.lru_cache(user_function)¶
- @functools.lru_cache(maxsize=128, typed=False)
関数をメモ化用の呼び出し可能オブジェクトでラップし、最近の呼び出し最大 maxsize 回まで保存するするデコレータです。高価な関数や I/O に束縛されている関数を定期的に同じ引数で呼び出すときに、時間を節約できます。
キャッシュはスレッドセーフなので、ラップされた関数を複数のスレッドで使用できます。 これは、同時に更新が行われても、内部のデータ構造の整合性が保たれるという意味です。
最初の呼び出しが完了して結果がキャッシュされるまでに別のスレッドから呼び出された場合、ラップされた関数が複数回呼び出される可能性があります。
結果のキャッシュには辞書が使われるので、関数の位置引数およびキーワード引数は ハッシュ可能 でなくてはなりません。
引数のパターンが異なると、別の呼び出しとして扱われ、個別のキャッシュエントリーになることがあります。 例えば、
f(a=1, b=2)とf(b=2, a=1)はキーワード引数の順序が異なるため、2 つの別々のキャッシュエントリーになる可能性があります。user_function が指定された場合、それは呼び出し可能でなければなりません。これにより lru_cache デコレータがユーザー関数に直接適用できるようになります。このとき maxsize の値はデフォルトの 128 となります:
@lru_cache def count_vowels(sentence): return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')
maxsize が
Noneに設定された場合は、LRU 機能は無効化され、キャッシュは際限無く大きくなります。typed を真にすると、型の異なる関数の引数は別々にキャッシュされます。 typed が偽の場合、通常は同等の呼び出しとして扱われ、結果は 1 つだけキャッシュされます。 (str や int など、typed が偽でも別々にキャッシュされる型もあります。)
Note, type specificity applies only to the function's immediate arguments rather than their contents. The scalar arguments,
Decimal(42)andFraction(42)are treated as distinct calls with distinct results. In contrast, the tuple arguments('answer', Decimal(42))and('answer', Fraction(42))are treated as equivalent.ラップされた関数には、maxsize と typed の値を示す新しい
dictを返すcache_parameters()関数が追加されます。 これは情報を得るためだけのもので、値を変更しても効果はありません。To help measure the effectiveness of the cache and tune the maxsize parameter, the wrapped function is instrumented with a
cache_info()function that returns a named tuple showing hits, misses, maxsize and currsize.The decorator also provides a
cache_clear()function for clearing or invalidating the cache.ラップされた元の関数には
__wrapped__属性でアクセスできます。 これは内観やキャッシュの回避、別のキャッシュによる関数のラップし直しに便利です。引数と戻り値への参照は、古くなってキャッシュから削除されるか、キャッシュが消去されるまで保持されます。
メソッドをキャッシュする場合、インスタンスの引数
selfもキャッシュに含まれます。 メソッド呼び出しをキャッシュするには どうしたらいいですか? を参照してください。LRU (least recently used) キャッシュ は、最近の呼び出しから今後の呼び出しをよく予測できる場合に最も効果を発揮します (例えば、ニュースサーバーで人気の記事は日ごとに変わる傾向があります)。 キャッシュのサイズ制限により、ウェブサーバーなど長時間実行するプロセスでもキャッシュが際限なく増えることを防ぎます。
一般に LRU キャッシュは、以前計算した値を再利用したい場合にのみ使ってください。 したがって、副作用のある関数、呼び出すたびに別のミュータブルなオブジェクトを作る必要のある関数 (ジェネレータや非同期関数など)、time() や random() のような純粋でない関数をキャッシュしても意味がありません。
静的 web コンテンツ の LRU キャッシュの例:
@lru_cache(maxsize=32) def get_pep(num): 'Python Enhancement Proposal のテキストを取得する' resource = f'https://peps.python.org/pep-{num:04d}' try: with urllib.request.urlopen(resource) as s: return s.read() except urllib.error.HTTPError: return 'Not Found' >>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991: ... pep = get_pep(n) ... print(n, len(pep)) >>> get_pep.cache_info() CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)
キャッシュを使って 動的計画法 の技法を実装し、フィボナッチ数 を効率よく計算する例:
@lru_cache(maxsize=None) def fib(n): if n < 2: return n return fib(n-1) + fib(n-2) >>> [fib(n) for n in range(16)] [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610] >>> fib.cache_info() CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
Added in version 3.2.
バージョン 3.3 で変更: typed オプションが追加されました。
バージョン 3.8 で変更: user_function オプションが追加されました。
バージョン 3.9 で変更:
cache_parameters()関数が追加されました。
- @functools.total_ordering¶
ひとつ以上の拡張順序比較メソッド (rich comparison ordering methods) を定義したクラスを受け取り、残りを実装するクラスデコレータです。このデコレータは全ての拡張順序比較演算をサポートするための労力を軽減します:
The class must define one of
__lt__(),__le__(),__gt__(), or__ge__(). In addition, the class should supply an__eq__()method.例えば:
@total_ordering class Student: def _is_valid_operand(self, other): return (hasattr(other, "lastname") and hasattr(other, "firstname")) def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return ((self.lastname.lower(), self.firstname.lower()) == (other.lastname.lower(), other.firstname.lower())) def __lt__(self, other): if not self._is_valid_operand(other): return NotImplemented return ((self.lastname.lower(), self.firstname.lower()) < (other.lastname.lower(), other.firstname.lower()))
注釈
このデコレータにより、このデコレータにより、行儀の良い(well behaved)全順序型を簡単に作ることができますが、その代償として実行速度は遅く、比較メソッドの演繹によってのスタックトレースは複雑になります。性能ベンチマークにより、これがアプリケーションのボトルネックになっていることがわかった場合は、代わりに 6 つの拡張比較メソッドをすべて実装すれば、簡単にスピードアップを図れるでしょう。
注釈
このデコレータは、クラス またはそのスーパークラス で宣言されているメソッドをオーバーライドしません。 つまり、スーパークラスが比較演算子を定義している場合は、元のメソッドが抽象メソッドであっても total_ordering はそれを再実装しません。
Added in version 3.2.
バージョン 3.4 で変更: 認識できない型に対して、元の比較関数が
NotImplementedを返すことをサポートしました。
- functools.partial(func, /, *args, **keywords)¶
新しい partial オブジェクト を返します。このオブジェクトは呼び出されると位置引数 args とキーワード引数 keywords 付きで呼び出された func のように振る舞います。呼び出しに際してさらなる引数が渡された場合、それらは args に付け加えられます。追加のキーワード引数が渡された場合には、それらで keywords を拡張または上書きします。おおよそ次のコードと等価です:
def partial(func, /, *args, **keywords): def newfunc(*fargs, **fkeywords): newkeywords = {**keywords, **fkeywords} return func(*args, *fargs, **newkeywords) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfunc
The
partial()is used for partial function application which "freezes" some portion of a function's arguments and/or keywords resulting in a new object with a simplified signature. For example,partial()can be used to create a callable that behaves like theint()function where the base argument defaults to two:>>> from functools import partial >>> basetwo = partial(int, base=2) >>> basetwo.__doc__ = 'Convert base 2 string to an int.' >>> basetwo('10010') 18
- class functools.partialmethod(func, /, *args, **keywords)¶
partialと似た動作をする新しいpartialmethod記述子 (デスクリプタ) を返します。直接呼び出しではなく、メソッド定義としての使用が目的であることのみが、partial とは異なります。func は、descriptor または呼び出し可能オブジェクトである必要があります (通常の関数など、両方の性質を持つオブジェクトは記述子として扱われます。)
When func is a descriptor (such as a normal Python function,
classmethod(),staticmethod(),abstractmethod()or another instance ofpartialmethod), calls to__get__are delegated to the underlying descriptor, and an appropriate partial object returned as the result.func が記述子ではない呼び出し可能オブジェクトの場合、適切な束縛メソッドを動的に作成します。 これは通常の Python 関数をメソッドとして使う場合と同じように動作します。 self 引数は、
partialmethodコンストラクタに渡した args や keywords よりも前に、最初の位置引数として挿入されます。以下はプログラム例です:
>>> class Cell: ... def __init__(self): ... self._alive = False ... @property ... def alive(self): ... return self._alive ... def set_state(self, state): ... self._alive = bool(state) ... set_alive = partialmethod(set_state, True) ... set_dead = partialmethod(set_state, False) ... >>> c = Cell() >>> c.alive False >>> c.set_alive() >>> c.alive True
Added in version 3.4.
- functools.reduce(function, iterable, [initial, ]/)¶
2 つの引数を取る function を、iterable の要素に左から右へ累積的に適用し、単一の値にまとめます。 例えば
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])は((((1+2)+3)+4)+5)を計算します。 左の引数 x は累積した値、右の引数 y は iterable から得た、更新に使う値です。 省略可能な initial を指定すると、計算時にiterableの要素より前に置かれ、iterableが空の場合のデフォルト値にもなります。 initial を指定せず、iterable に要素が 1 つしかない場合は、その要素を返します。およそ次と等価です:
initial_missing = object() def reduce(function, iterable, initial=initial_missing, /): it = iter(iterable) if initial is initial_missing: value = next(it) else: value = initial for element in it: value = function(value, element) return value
全ての中間値を返すイテレータについては
itertools.accumulate()を参照してください。
- @functools.singledispatch¶
関数を シングルディスパッチ ジェネリック関数 に変換します。
ジェネリック関数を定義するには、
@singledispatchデコレータを付けます。@singledispatchで関数を定義する場合、ディスパッチは最初の引数の型に基づく点に注意してください。>>> from functools import singledispatch >>> @singledispatch ... def fun(arg, verbose=False): ... if verbose: ... print("Let me just say,", end=" ") ... print(arg)
To add overloaded implementations to the function, use the
register()attribute of the generic function, which can be used as a decorator. For functions annotated with types, the decorator will infer the type of the first argument automatically:>>> @fun.register ... def _(arg: int, verbose=False): ... if verbose: ... print("Strength in numbers, eh?", end=" ") ... print(arg) ... >>> @fun.register ... def _(arg: list, verbose=False): ... if verbose: ... print("Enumerate this:") ... for i, elem in enumerate(arg): ... print(i, elem)
types.UnionTypeandtyping.Unioncan also be used:>>> @fun.register ... def _(arg: int | float, verbose=False): ... if verbose: ... print("Strength in numbers, eh?", end=" ") ... print(arg) ... >>> from typing import Union >>> @fun.register ... def _(arg: Union[list, set], verbose=False): ... if verbose: ... print("Enumerate this:") ... for i, elem in enumerate(arg): ... print(i, elem) ...
型アノテーションを使っていないコードについては、デコレータに適切な型引数を明示的に渡せます:
>>> @fun.register(complex) ... def _(arg, verbose=False): ... if verbose: ... print("Better than complicated.", end=" ") ... print(arg.real, arg.imag) ...
コレクションの型 (例:
list) に基づいてディスパッチし、その要素には型ヒント (例:list[int]) を付けたい場合は、ディスパッチに使う型をデコレータに明示的に渡し、型ヒントは関数定義に記述してください。>>> @fun.register(list) ... def _(arg: list[int], verbose=False): ... if verbose: ... print("Enumerate this:") ... for i, elem in enumerate(arg): ... print(i, elem)
注釈
実行時には、リストに含まれる要素の型に関係なく、リストのインスタンスに対して同じ実装を選びます。 つまり、
[1,2,3]と["foo", "bar", "baz"]は同じようにディスパッチされます。 この例のアノテーションは静的型チェッカーのためだけのもので、実行時には影響しません。To enable registering lambdas and pre-existing functions, the
register()attribute can also be used in a functional form:>>> def nothing(arg, verbose=False): ... print("Nothing.") ... >>> fun.register(type(None), nothing)
The
register()attribute returns the undecorated function. This enables decorator stacking,pickling, and the creation of unit tests for each variant independently:>>> @fun.register(float) ... @fun.register(Decimal) ... def fun_num(arg, verbose=False): ... if verbose: ... print("Half of your number:", end=" ") ... print(arg / 2) ... >>> fun_num is fun False
汎用関数は、呼び出されると 1 つ目の引数の型でディスパッチします:
>>> fun("Hello, world.") Hello, world. >>> fun("test.", verbose=True) Let me just say, test. >>> fun(42, verbose=True) Strength in numbers, eh? 42 >>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True) Enumerate this: 0 spam 1 spam 2 eggs 3 spam >>> fun(None) Nothing. >>> fun(1.23) 0.615
特定の型に対する実装が登録されていない場合、その型のメソッド解決順序に従って、より汎用的な実装を探します。
@singledispatchでデコレートした元の関数は、基底型objectに対する実装として登録されます。 そのため、より適切な実装が見つからなければ元の関数が使われます。抽象基底クラス (abstract base class) に対して実装が登録されると、基底クラスの仮想サブクラスに対してもその実装がディスパッチされます:
>>> from collections.abc import Mapping >>> @fun.register ... def _(arg: Mapping, verbose=False): ... if verbose: ... print("Keys & Values") ... for key, value in arg.items(): ... print(key, "=>", value) ... >>> fun({"a": "b"}) a => b
指定された型に対して、汎用関数がどの実装を選択するかを確認するには、
dispatch()属性を使用します:>>> fun.dispatch(float) <function fun_num at 0x1035a2840> >>> fun.dispatch(dict) # note: default implementation <function fun at 0x103fe0000>
登録されたすべての実装にアクセスするには、読み出し専用の
registry属性を使用します:>>> fun.registry.keys() dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>, <class 'decimal.Decimal'>, <class 'list'>, <class 'float'>]) >>> fun.registry[float] <function fun_num at 0x1035a2840> >>> fun.registry[object] <function fun at 0x103fe0000>
Added in version 3.4.
バージョン 3.7 で変更: The
register()attribute now supports using type annotations.バージョン 3.11 で変更: The
register()attribute now supportstypes.UnionTypeandtyping.Unionas type annotations.
- class functools.singledispatchmethod(func)¶
メソッドを シングルディスパッチ ジェネリック関数 に変換します。
To define a generic method, decorate it with the
@singledispatchmethoddecorator. When defining a method using@singledispatchmethod, note that the dispatch happens on the type of the first non-self or non-cls argument:class Negator: @singledispatchmethod def neg(self, arg): raise NotImplementedError("Cannot negate a") @neg.register def _(self, arg: int): return -arg @neg.register def _(self, arg: bool): return not arg
@singledispatchmethodsupports nesting with other decorators such as@classmethod. Note that to allow fordispatcher.register,singledispatchmethodmust be the outer most decorator. Here is theNegatorclass with thenegmethods bound to the class, rather than an instance of the class:class Negator: @singledispatchmethod @classmethod def neg(cls, arg): raise NotImplementedError("Cannot negate a") @neg.register @classmethod def _(cls, arg: int): return -arg @neg.register @classmethod def _(cls, arg: bool): return not arg
The same pattern can be used for other similar decorators:
@staticmethod,@~abc.abstractmethod, and others.Added in version 3.8.
- functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)¶
wrapper 関数を更新し、wrapped 関数と同じ属性を持つようにします。 省略可能な引数はタプルで、元の関数のどの属性をラッパー関数の同名の属性に直接代入するか、またラッパー関数のどの属性を元の関数の同名の属性で更新するかを指定します。 これらの引数のデフォルト値はモジュールレベルの定数
WRAPPER_ASSIGNMENTSとWRAPPER_UPDATESです。 前者はラッパー関数の__module__、__name__、__qualname__、__annotations__、__type_params__、およびドキュメンテーション文字列__doc__に代入し、後者は__dict__(インスタンスの辞書) を更新します。To allow access to the original function for introspection and other purposes (e.g. bypassing a caching decorator such as
@lru_cache), this function automatically adds a__wrapped__attribute to the wrapper that refers to the function being wrapped.この関数は主に関数を包んでラッパーを返す デコレータ 関数の中で使われるよう意図されています。もしラッパー関数がアップデートされないとすると、返される関数のメタデータは元の関数の定義ではなくラッパー関数の定義を反映してしまい、これは通常あまり有益ではありません。
update_wrapper()は、関数以外の呼び出し可能オブジェクトにも使えます。 assigned または updated で指名され、ラップされるオブジェクトに存在しない属性は、すべて無視されます (すなわち、ラッパー関数にそれらの属性を設定しようとは試みられません)。しかし、 updated で指名された属性がラッパー関数自身に存在しないならAttributeErrorが送出されます。バージョン 3.2 で変更:
__wrapped__属性が自動的に追加されるようになりました。__annotations__属性がデフォルトでコピーされるようになりました。 属性が欠けていてもAttributeErrorを送出しなくなりました。バージョン 3.4 で変更: ラップされた関数が
__wrapped__を定義している場合でも、__wrapped__が常にラップされた関数を参照するようになりました。 (bpo-17482 を参照)バージョン 3.12 で変更:
__type_params__属性がデフォルトでコピーされるようになりました。
- @functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)¶
これはラッパー関数を定義するときに
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'
Without the use of this decorator factory, the name of the example function would have been
'wrapper', and the docstring of the originalexample()would have been lost.
partial オブジェクト¶
partial オブジェクトは、 partial() 関数によって作られる呼び出し可能オブジェクトです。オブジェクトには読み出し専用の属性が三つあります:
partial objects are like function objects
in that they are callable, weak referenceable, and can have attributes.
There are some important differences. For instance, the
__name__ and function.__doc__ attributes
are not created automatically. Also, partial objects defined in
classes behave like static methods and do not transform into bound methods
during instance attribute look-up.