10.2. "functools" --- 高階関数と呼び出し可能オブジェクトの操作
**************************************************************

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

======================================================================

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

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

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))  # locale-aware sort order

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

   バージョン 3.2 で追加.

@functools.lru_cache(maxsize=128, typed=False)

   関数をメモ化用の呼び出し可能オブジェクトでラップし、最近の呼び出し
   最大 *maxsize* 回まで保存するするデコレータです。高価な関数や I/O
   に束縛されている関数を定期的に同じ引数で呼び出すときに、時間を節約
   できます。

   結果のキャッシュには辞書が使われるので、関数の位置引数およびキーワ
   ード引数はハッシュ可能でなくてはなりません。

   *maxsize* が "None" に設定された場合は、LRU 機能は無効化され、キャ
   ッシュは際限無く大きくなります。 LRU 機能は *maxsize* が 2 の累乗で
   あるときが最も効率的に動作します。

   *typed* が真に設定された場合は、関数の異なる型の引数が別々にキャッ
   シュされます。例えば、"f(3)" と "f(3.0)" は別の結果をもたらす別の呼
   び出しとして扱われます。

   キャッシュ効率の測定や *maxsize* パラメータの調整をしやすくするため
   、ラップされた関数には "cache_info()" 関数が追加されます。この関数
   は *hits*, *misses*, *maxsize*, *currsize* を示す *named tuple* を
   返します。マルチスレッド環境では、hits と misses は概算です。

   このデコレータは、キャッシュの削除と無効化のための "cache_clear()"
   関数も提供します。

   元々の基底の関数には、 "__wrapped__" 属性を通してアクセスできます。
   これはキャッシュを回避して、または関数を別のキャッシュでラップして
   、内観するのに便利です。

   LRU (least recently used) キャッシュ は、最新の呼び出しが次も呼び出
   される可能性が最も高い場合 (例えば、ニュースサーバーの最も人気のあ
   る記事は、毎日変わる傾向にあります) に最も効率が良くなります。キャ
   ッシュのサイズ制限は、キャッシュがウェブサーバーなどの長期間に渡る
   プロセスにおける限界を超えては大きくならないことを保証します。

   一般的には、 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

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

   引数のクラスは、 "__lt__()", "__le__()", "__gt__()", "__ge__()" の
   中からどれか1つと、 "__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)

   新しい 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

class functools.partialmethod(func, *args, **keywords)

   "partial" と似た動作をする新しい "partialmethod" 記述子 (デスクリプ
   タ) を返します。直接呼び出しではなく、メソッド定義としての使用が目
   的であることのみが、partial とは異なります。

   *func* は、*descriptor* または呼び出し可能オブジェクトである必要が
   あります (通常の関数など、両方の性質を持つオブジェクトは記述子とし
   て扱われます。)

   *func* が記述子 (Python の通常の関数、 "classmethod()"、
   "staticmethod()"、"abstractmethod()" または別の "partialmethod" の
   インスタンスなど) の場合、 "__get__" への呼び出しは下層の記述子に委
   譲され、返り値として適切な partial オブジェクト が返されます。

   *func* が記述子以外の呼び出し可能オブジェクトである場合、適切な束縛
   メソッドが動的に作成されます。この func は、メソッドとして使用され
   た場合、Python の通常の関数と同様に動作します。 "partialmethod" コ
   ンストラクタに *args* と *keywords* が渡されるよりも前に、 *self*
   引数が最初の位置引数として挿入されます。

   以下はプログラム例です:

      >>> 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 引数の *function* を *iterable* の要素に対して左から右に累積的に
   適用し、イテラブルを単一の値に縮約します。例えば、"reduce(lambda x,
   y: x+y, [1, 2, 3, 4, 5])" は "((((1+2)+3)+4)+5)" を計算します。左引
   数 *x* は累計の値で、右引数 *y* は *iterable* から取り出した更新値
   です。オプションの *initializer* が存在する場合、計算の際にイテラブ
   ルの先頭に置かれ、またイテラブルが空の場合のデフォルトになります。
   *initializer* が与えられておらず、*iterable* が単一の要素しか持って
   いない場合、最初の要素が返されます。

   およそ次と等価です:

      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

   関数を *シングルディスパッチ* *generic function* に変換します。

   汎用関数を定義するには、 "@singledispatch" デコレータでデコレートし
   ます。ディスパッチは 1 つ目の引数の型で行われることに注意して、関数
   を次のように作成してください:

      >>> 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()" 属性を関数形式で使用すると、lambda 関数と既存の関数の
   登録を有効にできます:

      >>> def nothing(arg, verbose=False):
      ...     print("Nothing.")
      ...
      >>> fun.register(type(None), nothing)

   The "register()" attribute returns the undecorated function which
   enables decorator stacking, pickling, as well as creating 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" 型に
   登録されます。これは、他によりよい実装が見つからないことを意味しま
   す。

   指定された型に対して、汎用関数がどの実装を選択するかを確認するには
   、"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__" が常にラップされた関数を参照するよう
   になりました。(bpo-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()" のドキュメンテーション文字列は
   失われてしまいます。


10.2.1. "partial" オブジェクト
==============================

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

partial.func

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

partial.args

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

partial.keywords

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

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