functools —呼び出し可能なオブジェクトの高階関数と操作—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.8/library/functools
移動先:案内検索

functools —呼び出し可能なオブジェクトに対する高階関数と操作

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



functools モジュールは、高階関数、つまり他の関数に作用するか、他の関数を返す関数用です。 一般に、このモジュールでは、呼び出し可能なオブジェクトを関数として扱うことができます。

functools モジュールは、次の関数を定義します。

@functools.cached_property(func)

クラスのメソッドをプロパティに変換します。このプロパティの値は一度計算されてから、インスタンスの存続期間中、通常の属性としてキャッシュされます。 property()に似ていますが、キャッシュが追加されています。 それ以外の場合は事実上不変であるインスタンスの高価な計算プロパティに役立ちます。

例:

class DataSet:
    def __init__(self, sequence_of_numbers):
        self._data = sequence_of_numbers

    @cached_property
    def stdev(self):
        return statistics.stdev(self._data)

    @cached_property
    def variance(self):
        return statistics.variance(self._data)

バージョン3.8の新機能。

ノート

このデコレータでは、各インスタンスの__dict__属性が可変マッピングである必要があります。 これは、メタクラス(タイプインスタンスの__dict__属性はクラス名前空間の読み取り専用プロキシであるため)や、__slots__を指定するタイプなどの一部のタイプでは機能しないことを意味します。定義されたスロットの1つとしてのX214X] (このようなクラスは、__dict__属性をまったく提供しません)。

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(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):
    sentence = sentence.casefold()
    return sum(sentence.count(vowel) for vowel in 'aeiou')

maxsizeNoneに設定されている場合、LRU機能は無効になり、キャッシュは無制限に増大する可能性があります。

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 オプションが追加されました。

バージョン3.8で変更: user_function オプションが追加されました。

@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, **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])

iterable の項目に、2つの引数の function を左から右に累積的に適用して、iterableを1つの値に減らします。 たとえば、reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])((((1+2)+3)+4)+5)を計算します。 左の引数 x は累積値であり、右の引数 yiterable からの更新値です。 オプションの initializer が存在する場合、計算ではiterableの項目の前に配置され、iterableが空の場合のデフォルトとして機能します。 initializer が指定されておらず、 iterable にアイテムが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

すべての中間値を生成するイテレータについては、 itertools.accumulate()を参照してください。

@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
... 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)

型注釈を使用しないコードの場合、適切な型引数をデコレータ自体に明示的に渡すことができます。

>>> @fun.register(complex)
... def _(arg, verbose=False):
...     if verbose:
...         print("Better than complicated.", end=" ")
...     print(arg.real, arg.imag)
...

ラムダと既存の関数の登録を有効にするために、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の新機能。

バージョン3.7で変更: register()属性は、型注釈の使用をサポートします。

class functools.singledispatchmethod(func)

メソッドをシングルディスパッチ ジェネリック関数に変換します。

一般的なメソッドを定義するには、@singledispatchmethodデコレータでデコレートします。 ディスパッチは最初のnon-selfまたはnon-cls引数の型で発生することに注意してください。それに応じて、関数を作成してください。

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

@singledispatchmethodは、@classmethodなどの他のデコレータとのネストをサポートします。 dispatcher.registerを許可するには、singledispatchmethod最も外側のデコレータである必要があることに注意してください。 これがNegatorクラスで、negメソッドがクラスにバインドされています。

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

同じパターンを他の同様のデコレータにも使用できます:staticmethodabstractmethodなど。

バージョン3.8の新機能。

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は失われます。

部分的オブジェクト

partial オブジェクトは、 partial()によって作成された呼び出し可能なオブジェクトです。 これらには、3つの読み取り専用属性があります。

partial.func
呼び出し可能なオブジェクトまたは関数。 partial オブジェクトへの呼び出しは、新しい引数とキーワードを使用して func に転送されます。
partial.args
partial オブジェクト呼び出しに提供される位置引数の前に付加される左端の位置引数。
partial.keywords
partial オブジェクトが呼び出されたときに提供されるキーワード引数。

partial オブジェクトは、呼び出し可能、弱い参照可能、および属性を持つことができるという点でfunctionオブジェクトに似ています。 いくつかの重要な違いがあります。 たとえば、 __ name __ および__doc__属性は自動的に作成されません。 また、クラスで定義された partial オブジェクトは静的メソッドのように動作し、インスタンス属性のルックアップ中にバインドされたメソッドに変換されません。