"functools" --- 可呼叫物件上的高階函式與操作
********************************************

**原始碼：**Lib/functools.py

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

"functools" 模組用於高階函式：作用於或回傳其他函式的函式。一般來說，任
何可呼叫物件都可以被視為用於此模組的函式。

"functools" 模組定義了以下函式：

@functools.cache(user_function)

   簡單的輕量級無繫結函式快取 (Simple lightweight unbounded function
   cache)。有時稱之為 "memoize"（記憶化）。

   和 "lru_cache(maxsize=None)" 回傳相同的值，為函式引數建立一個字典查
   找的薄包裝器。因為它永遠不需要丟棄舊值，所以這比有大小限制的
   "lru_cache()" 更小、更快。

   舉例來說：

      @cache
      def factorial(n):
          return n * factorial(n-1) if n else 1

      >>> factorial(10)      # 沒有先前的快取結果，會進行 11 次遞迴呼叫
      3628800
      >>> factorial(5)       # 沒有新的呼叫，直接回傳被快取起來的結果
      120
      >>> factorial(12)      # 兩次新的遞迴呼叫，factorial(10) 有被快取起來
      479001600

   該快取是執行緒安全的 (threadsafe)，因此包裝的函式可以在多個執行緒中
   使用。這意味著底層資料結構在並行更新期間將保持連貫 (coherent)。

   如果另一個執行緒在初始呼叫完成並快取之前進行額外的呼叫，則包裝的函
   式可能會被多次呼叫。

   在 3.9 版被加入.

@functools.cached_property(func)

   將類別的一個方法轉換為屬性 (property)，其值會計算一次，然後在實例的
   生命週期內快取為普通屬性。類似 "property()"，但增加了快取機制。對於
   除使用該裝飾器的屬性外實質上幾乎是不可變 (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)

   "cached_property()" 的機制與 "property()" 有所不同。除非定義了
   setter，否則常規屬性會阻止屬性的寫入。相反地，*cached_property* 則
   允許寫入。

   *cached_property* 裝飾器僅在查找時且僅在同名屬性不存在時運行。當它
   運行時，*cached_property* 會寫入同名的屬性。後續屬性讀取和寫入優先
   於 *cached_property* 方法，並且它的工作方式與普通屬性類似。

   可以透過刪除屬性來清除快取的值，這使得 *cached_property* 方法可以再
   次運行。

   *cached_property* 無法防止多執行緒使用中可能出現的競爭條件 (race
   condition)。getter 函式可以在同一個實例上運行多次，最後一次運行會設
   定快取的值。所以快取的屬性最好是冪等的 (idempotent)，或者在一個實例
   上運行多次不會有害，就不會有問題。如果同步是必要的，請在裝飾的
   getter 函式內部或在快取的屬性存取周圍實作必要的鎖。

   請注意，此裝飾器會干擾 **PEP 412** 金鑰共用字典的操作。這意味著實例
   字典可能比平常佔用更多的空間。

   此外，此裝飾器要求每個實例上的 "__dict__" 屬性是可變對映 (mutable
   mapping)。這意味著它不適用於某些型別，例如元類別 (metaclass)（因為
   型別實例上的 "__dict__" 屬性是類別命名空間的唯讀代理），以及那些指
   定 "__slots__" 而不包含 "__dict__" 的型別作為有定義的插槽之一（因為
   此種類別根本不提供 "__dict__" 屬性）。

   如果可變對映不可用或需要金鑰共享以節省空間，則也可以透過在
   "lru_cache()" 之上堆疊 "property()" 來實作類似於
   "cached_property()" 的效果。請參閱如何快取方法呼叫？以了解有關這與
   "cached_property()" 間不同之處的更多詳細資訊。

   在 3.8 版被加入.

   在 3.12 版的變更: 在 Python 3.12 之前，"cached_property" 包含一個未
   以文件記錄的鎖，以確保在多執行緒使用中能保證 getter 函式對於每個實
   例只會執行一次。然而，鎖是針對每個屬性，而不是針對每個實例，這可能
   會導致無法被接受的嚴重鎖爭用 (lock contention)。在 Python 3.12+ 中
   ，此鎖已被刪除。

functools.cmp_to_key(func)

   將舊式比較函式轉換為*鍵函式*，能與接受鍵函式的工具一起使用（例如
   "sorted()"、"min()"、"max()"、"heapq.nlargest()"、
   "heapq.nsmallest()"、"itertools.groupby()"）。此函式主要作為轉換工
   具，用於從有支援使用比較函式的 Python 2 轉換成的程式。

   比較函式是任何能接受兩個引數、對它們進行比較，並回傳負數（小於）、
   零（相等）或正數（大於）的可呼叫物件。鍵函式是接受一個引數並回傳另
   一個用作排序鍵之值的可呼叫物件。

   範例：

      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 密集的函式時，它可以節省時
   間。

   該快取是執行緒安全的 (threadsafe)，因此包裝的函式可以在多個執行緒中
   使用。這意味著底層資料結構在並行更新期間將保持連貫 (coherent)。

   如果另一個執行緒在初始呼叫完成並快取之前進行額外的呼叫，則包裝的函
   式可能會被多次呼叫。

   由於字典用於快取結果，因此函式的位置引數和關鍵字引數必須是*可雜湊的
   *。

   不同的引數模式可以被認為是具有不同快取條目的不同呼叫。例如，"f(a=1,
   b=2)" 和 "f(b=2, a=1)" 的關鍵字引數順序不同，並且可能有兩個不同的快
   取條目。

   如果指定了 *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* 設定為 true，不同型別的函式引數將會被單獨快取起來。如
   果 *typed* 為 false，則實作通常會將它們視為等效呼叫，並且僅快取單一
   結果。（某些型別，例如 *str* 和 *int* 可能會被單獨快取起來，即使
   *typed* 為 false。）

   請注意，型別特異性 (type specificity) 僅適用於函式的直接引數而不是
   其內容。純量 (scalar) 引數 "Decimal(42)" 和 "Fraction(42)" 被視為具
   有不同結果的不同呼叫。相反地，元組引數 "('answer', Decimal(42))" 和
   "('answer', Fraction(42))" 被視為等效。

   包裝的函式使用一個 "cache_parameters()" 函式來進行偵測，該函式回傳
   一個新的 "dict" 以顯示 *maxsize* 和 *typed* 的值。這僅能顯示資訊，
   改變其值不會有任何效果。

   為了輔助測量快取的有效性並調整 *maxsize* 參數，包裝的函式使用了一個
   "cache_info()" 函式來做檢測，該函式會回傳一個*附名元組*來顯示
   *hits*、*misses*、*maxsize* 和 *currsize*。

   裝飾器還提供了一個 "cache_clear()" 函式來清除或使快取失效。

   原本的底層函式可以透過 "__wrapped__" 屬性存取。這對於要自我檢查
   (introspection)、繞過快取或使用不同的快取重新包裝函式時非常有用。

   快取會保留對引數和回傳值的參照，直到快取過時 (age out) 或快取被清除
   為止。

   如果方法被快取起來，則 "self" 實例引數將包含在快取中。請參閱如何快
   取方法呼叫？

   當最近的呼叫是即將發生之呼叫的最佳預測因子時（例如新聞伺服器上最受
   歡迎的文章往往每天都會發生變化），LRU (least recently used) 快取能
   發揮最好的效果。快取的大小限制可確保快取不會在長時間運行的行程（例
   如 Web 伺服器）上無限制地成長。

   一般來說，僅當你想要重複使用先前計算的值時才應使用 LRU 快取。因此，
   快取具有 side-effects 的函式、需要在每次呼叫時建立不同可變物件的函
   式（例如產生器和非同步函式）或不純函式（impure function，例如
   time() 或 random()）是沒有意義的。

   靜態網頁內容的 LRU 快取範例：

      @lru_cache(maxsize=32)
      def get_pep(num):
          '取得 Python 改進提案的文字'
          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)

   使用快取來實作動態規劃 (dynamic programming) 技法以有效率地計算費波
   那契數 (Fibonacci numbers) 的範例：

      @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* 選項。

   在 3.9 版的變更: 新增 "cache_parameters()" 函式。

@functools.total_ordering

   給定一個定義一個或多個 rich comparison 排序方法的類別，該類別裝飾器
   會提供其餘部分。這簡化了指定所有可能的 rich comparison 操作所涉及的
   工作：

   類別必須定義 "__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()))

   備註:

     雖然此裝飾器可以輕鬆建立能好好運作的完全有序型別 (totally ordered
     types)，但它*的確*以衍生比較方法的執行速度較慢和堆疊追蹤 (stack
     trace) 較複雜做為其代價。如果效能基準測試顯示這是給定應用程式的效
     能瓶頸，那麼實作全部六種 rich comparison 方法通常能輕鬆地提升速度
     。

   備註:

     此裝飾器不會嘗試覆寫類別*或其超類別 (superclass)*中宣告的方法。這
     意味著如果超類別定義了比較運算子，*total_ordering* 將不會再次實作
     它，即使原始方法是抽象的。

   在 3.2 版被加入.

   在 3.4 版的變更: 現在支援從底層對於未識別型別的比較函式回傳
   "NotImplemented"。

functools.Placeholder

   A singleton object used as a sentinel to reserve a place for
   positional arguments when calling "partial()" and
   "partialmethod()".

   在 3.14 版被加入.

functools.partial(func, /, *args, **keywords)

   回傳一個新的 partial 物件，它在被呼叫時的行為類似於使用位置引數
   *args* 和關鍵字引數 *keywords* 呼叫的 *func*。如果向呼叫提供更多引
   數，它們將被附加到 *args*。如果提供了額外的關鍵字引數，它們會擴充並
   覆寫 *keywords*。大致相當於：

      def partial(func, /, *args, **keywords):
          def newfunc(*more_args, **more_keywords):
              return func(*args, *more_args, **(keywords | more_keywords))
          newfunc.func = func
          newfunc.args = args
          newfunc.keywords = keywords
          return newfunc

   "partial()" 用於部分函式應用程序，它「凍結」函式引數和/或關鍵字的某
   些部分，從而產生具有簡化簽名的新物件。例如，"partial()" 可用來建立
   可呼叫函式，其行為類似於 "int()" 函式，其中 *base* 引數預設為 2：

      >>> basetwo = partial(int, base=2)
      >>> basetwo.__doc__ = 'Convert base 2 string to an int.'
      >>> basetwo('10010')
      18

   If "Placeholder" sentinels are present in *args*, they will be
   filled first when "partial()" is called. This makes it possible to
   pre-fill any positional argument with a call to "partial()";
   without "Placeholder", only the chosen number of leading positional
   arguments can be pre-filled.

   If any "Placeholder" sentinels are present, all must be filled at
   call time:

      >>> say_to_world = partial(print, Placeholder, Placeholder, "world!")
      >>> say_to_world('Hello', 'dear')
      Hello dear world!

   Calling "say_to_world('Hello')" raises a "TypeError", because only
   one positional argument is provided, but there are two placeholders
   that must be filled in.

   If "partial()" is applied to an existing "partial()" object,
   "Placeholder" sentinels of the input object are filled in with new
   positional arguments. A placeholder can be retained by inserting a
   new "Placeholder" sentinel to the place held by a previous
   "Placeholder":

      >>> from functools import partial, Placeholder as _
      >>> remove = partial(str.replace, _, _, '')
      >>> message = 'Hello, dear dear world!'
      >>> remove(message, ' dear')
      'Hello, world!'
      >>> remove_dear = partial(remove, _, ' dear')
      >>> remove_dear(message)
      'Hello, world!'
      >>> remove_first_dear = partial(remove_dear, _, 1)
      >>> remove_first_dear(message)
      'Hello, dear world!'

   "Placeholder" cannot be passed to "partial()" as a keyword
   argument.

   在 3.14 版的變更: Added support for "Placeholder" in positional
   arguments.

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

   回傳一個新的 "partialmethod" 描述器 (descriptor)，其行為類似於
   "partial"，只不過它被設計為用於方法定義而不能直接呼叫。

   *func* 必須是一個 *descriptor* 或可呼叫物件（兩者兼具的物件，就像普
   通函式一樣，會被當作描述器處理）。

   當 *func* 是描述器時（例如普通的 Python 函式、"classmethod()"、
   "staticmethod()"、"abstractmethod()" 或 "partialmethod" 的另一個實
   例），對 "__get__" 的呼叫將被委託 (delegated) 給底層描述器，且一個
   適當的 partial 物件會被作為結果回傳。

   當 *func* 是非描述器可呼叫物件 (non-descriptor callable) 時，會動態
   建立適當的繫結方法 (bound method)。當被作為方法使用時，其行為類似於
   普通的 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

   在 3.4 版被加入.

functools.reduce(function, iterable, /[, initial])

   從左到右，將兩個引數的 *function* 累加運用到 *iterable* 的項目上，
   從而將可疊代物件減少為單一值。例如，"reduce(lambda x, y: x+y, [1,
   2, 3, 4, 5])" 會計算出 "((((1+2)+3)+4)+5)"。左邊的引數 *x* 是累積值
   ，右邊的引數 *y* 是來自 *iterable* 的更新值。如果可選的 *initial*
   存在，則在計算中會將其放置在可疊代物件的項目之前，並在可疊代物件為
   空時作為預設值。如果未給定 *initial* 且 *iterable* 僅包含一個項目，
   則回傳第一個項目。

   大致相當於：

      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()" 以了解產生 (yield) 所有中間值
   (intermediate value) 的疊代器。

   在 3.14 版的變更: *initial* is now supported as a keyword argument.

@functools.singledispatch

   將函式轉換為*單一調度**泛型函式*。

   若要定義泛型函式，請使用 "@singledispatch" 裝飾器對其裝飾。請注意，
   使用 "@singledispatch" 定義函式時，分派調度 (dispatch) 是發生在第一
   個引數的型別上：

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

   也可以使用 "typing.Union"：

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

   For code that dispatches on a collections type (e.g., "list"), but
   wants to typehint the items of the collection (e.g., "list[int]"),
   the dispatch type should be passed explicitly to the decorator
   itself with the typehint going into the function definition:

      >>> @fun.register(list)
      ... def _(arg: list[int], verbose=False):
      ...     if verbose:
      ...         print("Enumerate this:")
      ...     for i, elem in enumerate(arg):
      ...         print(i, elem)

   備註:

     At runtime the function will dispatch on an instance of a list
     regardless of the type contained within the list i.e. "[1,2,3]"
     will be dispatched the same as "["foo", "bar", "baz"]". The
     annotation provided in this example is for static type checkers
     only and has no runtime impact.

   若要啟用註冊 *lambdas* 和預先存在的函式，"register()" 屬性也能以函
   式形式使用：

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

   "register()" 屬性回傳未加裝飾器的函式。這讓使得裝飾器堆疊
   (decorator stacking)、"pickling" 以及為每個變體獨立建立單元測試成為
   可能：

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

   如果沒有為特定型別註冊實作，則使用其方法解析順序 (method resolution
   order) 來尋找更通用的實作。用 "@singledispatch" 裝飾的原始函式是為
   基底 "object" 型別註冊的，這意味著如果沒有找到更好的實作就會使用它
   。

   如果一個實作有被註冊到一個*抽象基底類別*，則基底類別的虛擬子類別將
   被分派到該實作：

      >>> 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>

   在 3.4 版被加入.

   在 3.7 版的變更: "register()" 屬性現在支援使用型別註釋。

   在 3.11 版的變更: "register()" 屬性現在支援使用 "typing.Union" 型別
   註釋。

class functools.singledispatchmethod(func)

   將方法轉換為*單一調度**泛型函式*。

   若要定義泛型方法，請使用 "@singledispatchmethod" 裝飾器對其裝飾。請
   注意，使用 "@singledispatchmethod" 定義方法時，分派調度是發生在第一
   個非 *self* 或非 *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" 支援與其他裝飾器巢狀使用 (nesting)，例如
   "@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

   相同的模式可用於其他類似的裝飾器："@staticmethod"、
   "@~abc.abstractmethod" 等。

   在 3.8 版被加入.

functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

   更新 *wrapper* 函式，使其看起來像 *wrapped* 函式。可選引數是元組，
   用於指定原始函式的哪些屬性直接賦值給包裝函式上的匹配屬性，以及包裝
   函式的哪些屬性使用原始函式中的對應屬性進行更新。這些引數的預設值是
   模組層級的常數 "WRAPPER_ASSIGNMENTS"（它賦值給包裝函式的
   "__module__"、"__name__"、"__qualname__"、"__annotations__"、
   "__type_params__" 和 "__doc__" 文件字串 (docstring））和
   "WRAPPER_UPDATES"（更新包裝器函式的 "__dict__"，即實例字典）。

   為了允許出於內省 (introspection) 和其他目的所對原始函式的存取（例如
   繞過快取裝飾器，如 "lru_cache()"），此函式會自動向包裝器新增
   "__wrapped__" 屬性，該包裝器參照被包裝的函式。

   此函式的主要用途是在 *decorator* 函式中，它包裝函式並回傳包裝器。如
   果包裝器函式未更新，則回傳函式的元資料 (metadata) 將反映包裝器定義
   而非原始函式定義，這通常不太會有幫助。

   "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'

   如果不使用這個裝飾器工廠 (decorator factory)，範例函式的名稱將會是
   "'wrapper'"，並且原始 "example()" 的文件字串將會遺失。


"partial" 物件
==============

"partial" 物件是由 "partial()" 所建立的可呼叫物件。它們有三個唯讀屬性
：

partial.func

   一個可呼叫的物件或函式。對 "partial" 物件的呼叫將被轉送到帶有新引數
   和關鍵字的 "func"。

partial.args

   最左邊的位置引數將會被加入到提供給 "partial" 物件呼叫的位置引數的前
   面。

partial.keywords

   呼叫 "partial" 物件時將提供的關鍵字引數。

"partial" 物件與函式物件類似，因為它們是可呼叫的、可弱參照的 (weak
referencable) 且可以具有屬性的。但有一些重要的區別，例如，"__name__"
和 "__doc__" 屬性不會自動建立。
