10.2. functools —呼び出し可能なオブジェクトの高階関数と操作—Pythonドキュメント
10.2。 functools —呼び出し可能なオブジェクトに対する高階関数と操作
ソースコード: :source: `Lib / functools.py`
functools モジュールは、高階関数、つまり他の関数に作用するか、他の関数を返す関数用です。 一般に、このモジュールでは、呼び出し可能なオブジェクトを関数として扱うことができます。
functools モジュールは、次の関数を定義します。
- functools.cmp_to_key(func)
古いスタイルの比較関数をキー関数に変換します。 キー関数を受け入れるツール( sorted()、 min()、 max()、 heapq.nlargest()[X122Xなど)で使用されます]、 heapq.nsmallest()、 itertools.groupby())。 この関数は主に、比較関数の使用をサポートするPython2から変換されるプログラムの移行ツールとして使用されます。
比較関数は、2つの引数を受け入れ、それらを比較し、より小さい場合は負の数、等しい場合はゼロ、より大きい場合は正の数を返す呼び出し可能関数です。 キー関数は、1つの引数を受け入れ、ソートキーとして使用される別の値を返す呼び出し可能関数です。
例:
sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order
並べ替えの例と簡単な並べ替えチュートリアルについては、並べ替え方法を参照してください。
バージョン3.2の新機能。
- @functools.lru_cache(maxsize=128, typed=False)
maxsize 最新の呼び出しまで保存するメモ化呼び出し可能オブジェクトで関数をラップするデコレータ。 高価な関数またはI / Oバウンド関数が同じ引数で定期的に呼び出されると、時間を節約できます。
ディクショナリは結果をキャッシュするために使用されるため、関数の定位置引数とキーワード引数はハッシュ可能である必要があります。
maxsize が
None
に設定されている場合、LRU機能は無効になり、キャッシュは無制限に増大する可能性があります。 LRU機能は、 maxsize が2の累乗である場合に最高のパフォーマンスを発揮します。typed がtrueに設定されている場合、異なるタイプの関数引数は個別にキャッシュされます。 たとえば、
f(3)
とf(3.0)
は別個の呼び出しとして扱われ、別個の結果が得られます。キャッシュの有効性を測定し、 maxsize パラメーターを調整するために、ラップされた関数には、ヒットを示す名前付きタプルを返す
cache_info()
関数が組み込まれています。 、ミス、最大サイズおよびカーソルサイズ。 マルチスレッド環境では、ヒットとミスは概算です。デコレータは、キャッシュをクリアまたは無効にするための
cache_clear()
関数も提供します。元の基礎となる関数には、
__wrapped__
属性を介してアクセスできます。 これは、イントロスペクション、キャッシュのバイパス、または関数を別のキャッシュで再ラップする場合に役立ちます。LRU(最近使用されていない)キャッシュは、最新の通話が今後の通話の最良の予測因子である場合に最適に機能します(たとえば、ニュースサーバーで最も人気のある記事は毎日変更される傾向があります)。 キャッシュのサイズ制限により、Webサーバーなどの長時間実行されるプロセスに制限されることなくキャッシュが増大することはありません。
一般に、LRUキャッシュは、以前に計算された値を再利用する場合にのみ使用する必要があります。 したがって、副作用のある関数、呼び出しごとに個別の可変オブジェクトを作成する必要のある関数、またはtime()やrandom()などの不純な関数をキャッシュすることは意味がありません。
静的WebコンテンツのLRUキャッシュの例:
@lru_cache(maxsize=32) def get_pep(num): 'Retrieve text of a Python Enhancement Proposal' resource = 'http://www.python.org/dev/peps/pep-%04d/' % num 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)
バージョン3.2の新機能。
バージョン3.3で変更: typed オプションが追加されました。
- @functools.total_ordering
1つ以上の豊富な比較順序付けメソッドを定義するクラスが与えられると、このクラスデコレータが残りを提供します。 これにより、可能なすべての豊富な比較操作を指定する作業が簡素化されます。
クラスは、
__lt__()
、__le__()
、__gt__()
、または__ge__()
のいずれかを定義する必要があります。 さらに、クラスは__eq__()
メソッドを提供する必要があります。例えば:
@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()))
ノート
このデコレータを使用すると、正常に動作する完全に順序付けられた型を簡単に作成できますが、実行が遅くなり、派生した比較メソッドのスタックトレースが複雑になるという犠牲が伴います。 パフォーマンスベンチマークがこれが特定のアプリケーションのボトルネックであることを示している場合、代わりに6つの豊富な比較方法すべてを実装すると、簡単に速度を上げることができます。
バージョン3.2の新機能。
バージョン3.4で変更:認識されない型の基になる比較関数からNotImplementedを返すことがサポートされるようになりました。
- functools.partial(func, *args, **keywords)
新しい部分オブジェクトを返します。これは、呼び出されると、位置引数 args およびキーワード引数キーワードで呼び出された func のように動作します。 呼び出しにさらに引数が指定されると、それらは args に追加されます。 追加のキーワード引数が指定されている場合、それらはキーワードを拡張およびオーバーライドします。 ほぼ同等:
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()を使用して、 int()関数のように動作する呼び出し可能オブジェクトを作成できます。 base 引数のデフォルトは次の2つです。
>>> 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 記述子を返します。ただし、直接呼び出すことはできず、メソッド定義として使用するように設計されています。
func は、記述子または呼び出し可能である必要があります(通常の関数と同様に、両方のオブジェクトは記述子として処理されます)。
func が記述子(通常のPython関数、 classmethod()、 staticmethod()、
abstractmethod()
の別のインスタンスなど)の場合X152X] partialmethod )、__get__
の呼び出しは基になる記述子に委任され、結果として適切な partial object が返されます。func が記述子以外の呼び出し可能である場合、適切なバインドされたメソッドが動的に作成されます。 これは、メソッドとして使用すると通常のPython関数のように動作します。引数およびキーワードが指定される前であっても、 self 引数が最初の位置引数として挿入されます。 partialmethod コンストラクターに。
例:
>>> class Cell(object): ... 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
バージョン3.4の新機能。
- functools.reduce(function, iterable[, initializer])
シーケンスの項目に、2つの引数の関数を左から右に累積的に適用して、シーケンスを1つの値に減らします。 たとえば、
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
は((((1+2)+3)+4)+5)
を計算します。 左の引数 x は累積値であり、右の引数 y はシーケンスからの更新値です。 オプションの初期化子が存在する場合、計算ではシーケンスの項目の前に配置され、シーケンスが空の場合のデフォルトとして機能します。 initializer が指定されておらず、 sequence にアイテムが1つしかない場合、最初のアイテムが返されます。ほぼ同等:
def reduce(function, iterable, initializer=None): it = iter(iterable) if initializer is None: value = next(it) else: value = initializer for element in it: value = function(value, element) return value
- @functools.singledispatch
関数をシングルディスパッチ ジェネリック関数に変換します。
ジェネリック関数を定義するには、
@singledispatch
デコレータでデコレートします。 ディスパッチは最初の引数の型で発生することに注意してください。それに応じて関数を作成してください。>>> from functools import singledispatch >>> @singledispatch ... def fun(arg, verbose=False): ... if verbose: ... print("Let me just say,", end=" ") ... print(arg)
オーバーロードされた実装を関数に追加するには、ジェネリック関数の
register()
属性を使用します。 これはデコレータであり、型パラメータを取り、その型の操作を実装する関数を装飾します。>>> @fun.register(int) ... def _(arg, verbose=False): ... if verbose: ... print("Strength in numbers, eh?", end=" ") ... print(arg) ... >>> @fun.register(list) ... def _(arg, verbose=False): ... if verbose: ... print("Enumerate this:") ... for i, elem in enumerate(arg): ... print(i, elem)
ラムダと既存の関数の登録を有効にするために、
register()
属性を関数形式で使用できます。>>> def nothing(arg, verbose=False): ... print("Nothing.") ... >>> fun.register(type(None), nothing)
register()
属性は、デコレータのスタッキング、ピクルス化、および各バリアントの単体テストの作成を個別に可能にする、装飾されていない関数を返します。>>> @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
呼び出されると、ジェネリック関数は最初の引数の型でディスパッチします。
>>> 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
タイプに登録されています。つまり、より適切な実装が見つからない場合に使用されます。ジェネリック関数が特定のタイプに対してどの実装を選択するかを確認するには、
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>
バージョン3.4の新機能。
- functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
wrapper 関数を wrapped 関数のように更新します。 オプションの引数は、元の関数のどの属性がラッパー関数の一致する属性に直接割り当てられ、ラッパー関数のどの属性が元の関数の対応する属性で更新されるかを指定するタプルです。 これらの引数のデフォルト値は、モジュールレベルの定数
WRAPPER_ASSIGNMENTS
(ラッパー関数の__module__
、__name__
、__qualname__
、__annotations__
に割り当てられます)です。 ]および__doc__
、ドキュメント文字列)およびWRAPPER_UPDATES
(ラッパー関数の__dict__
を更新します。 インスタンス辞書)。イントロスペクションやその他の目的で元の機能にアクセスできるようにするため(例: lru_cache())などのキャッシングデコレータをバイパスすると、この関数は、ラップされている関数を参照するラッパーに
__wrapped__
属性を自動的に追加します。この関数の主な使用目的は、装飾された関数をラップしてラッパーを返すデコレータ関数です。 ラッパー関数が更新されていない場合、返される関数のメタデータは、元の関数定義ではなくラッパー定義を反映します。これは通常、役に立たないものです。
update_wrapper()は、関数以外の呼び出し可能オブジェクトで使用できます。 ラップされるオブジェクトから欠落している assigned または updated で指定された属性はすべて無視されます(つまり、 この関数は、ラッパー関数にそれらを設定しようとはしません)。 ラッパー関数自体に updated で指定された属性がない場合、 AttributeError は引き続き発生します。
バージョン3.2の新機能:
__wrapped__
属性の自動追加。バージョン3.2の新機能:デフォルトで
__annotations__
属性のコピー。バージョン3.2で変更:属性がない場合、 AttributeError がトリガーされなくなりました。
バージョン3.4で変更:
__wrapped__
属性は、その関数が__wrapped__
属性を定義している場合でも、常にラップされた関数を参照するようになりました。 (:issue: `17482` を参照)
- @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'
このデコレータファクトリを使用しないと、サンプル関数の名前は
'wrapper'
になり、元のexample()
のdocstringは失われます。
10.2.1。 部分的オブジェクト
partial オブジェクトは、 partial()によって作成された呼び出し可能なオブジェクトです。 これらには、3つの読み取り専用属性があります。
- partial.args
- partial オブジェクト呼び出しに提供される位置引数の前に付加される左端の位置引数。
- partial.keywords
- partial オブジェクトが呼び出されたときに提供されるキーワード引数。
partial オブジェクトは、呼び出し可能、弱い参照可能、および属性を持つことができるという点でfunction
オブジェクトに似ています。 いくつかの重要な違いがあります。 たとえば、 __ name __ および__doc__
属性は自動的に作成されません。 また、クラスで定義された partial オブジェクトは静的メソッドのように動作し、インスタンス属性のルックアップ中にバインドされたメソッドに変換されません。