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

原始碼：Lib/functools.py

The `functools` module is for higher-order functions: functions that act on or return other functions. In general, any callable object can be treated as a function for the purposes of this module.

The `functools` module defines the following functions:

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

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

  Call-once behavior is not guaranteed because locks are not held during the function call. Potentially another call with the same arguments could occur while the first call is still running.

  在 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(word):
          return sum(word.count(vowel) for vowel in 'AEIOUaeiou')

  如果 maxsize 設定為 `None`，則 LRU 功能將被停用，且快取可以無限制地成長。

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

  Note, type specificity applies only to the function's immediate arguments rather than their contents. The scalar arguments, `Decimal(42)` and `Fraction(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.

  包裝的函式使用一個 `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 版被加入.

  在 3.15 版的變更: Added support of non-descriptor callables.

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__` 屬性不會自動建立。