Page cover

@total_ordering


允許我們僅透過 2 個 Class comparison 方法,來自動完成一套完整的 comparison operations (<<===!=>=>) 方法。@total_ordering 允許您專注於 Class 如何比較的核心邏輯,並讓 Python 處理繁瑣的樣板。 (顯著簡化您的程式碼)

Class 必須使用 __eq__ 和其他任一種方法 (__lt__ (小於)、__le__ (小於或等於)、__gt__ (大於)、__ge__ (大於或等於)),才能使用 @total_ordering

@total_ordering 不會嘗試覆蓋 (實現) 已經存在的 comparison 方法。

例如:繼承自 Class, SuperClasses or Abstract Class

大多數情況下,@total_ordering 增加的便利性,超過了它的性能開銷。

如果性能基準測試表明這會造成給定應用程序的瓶頸,那麼實施所有 comparison 方法 (6 種) 將能輕鬆地提升速度。

from functools import total_ordering

@total_ordering
class MyClass():
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        if isinstance(other, MyClass):
            return self.value == other.value
        return NotImplemented

    def __lt__(self, other):
        if isinstance(other, MyClass):
            return self.value < other.value
        return NotImplemented
a = MyClass(10)
b = MyClass(20)

print(a == b)  # False
print(a != b)  # True
print(a < b)   # True
print(a > b)   # False
print(a <= b)  # True
print(a >= b)  # False

comparison 方法:

self 參數是對調用該方法的實例的引用,而 other 參數是對我們要比較的實例的引用。

假設我們有一個 MyClass Class 和兩個實例 ab。當您在程式碼中調用 a < b 時,這實際上會被轉換為 a.__lt__(b)

實例 aself,實例 bother,因此 Class 中必須有 __lt__(self, other) 此方法。

@wraps()


@wraps()是一個 decorator factory,它將 update_wrapper() 應用於裝飾函數。 這通常用在裝飾器定義中,以確保保留被裝飾的原始函數的 metadata。

當我們創建一個 decorator 時,通常會用另一個函數替換或包裝(wrapper)一個函數。 因此原始函數的 metadata 會丟失 (name, docstring, module, parameter list...)。 如果我們需要訪問這些訊息,這就會出現問題 (例如:debugging 或檢查)。

這就是 @wraps() 派上用場的地方。 該 decorator 用於我們的 decorator 的定義中,以保存裝飾函數的 metadata。

範例 – 帶參數的 Decorators


有時,我們想要定義帶有參數的 decorators。 這需要另一層 wrapping。

在此示例中,repeat()是一個 decorator factory,它接受參數num_times。 它返回 decorator decorator_repeat(),而 decorator 又返回包裝函數。 @wraps(func) 保留了原始函數的 metadata。

範例 – Debugging


執行結果:

update_wrapper()


update_wrapper() 函數和 @functools.wraps() 裝飾器都具有相似的目的:使包裝函數在屬性方面,模仿被包裝函數(例如:__name____doc__...)。

區別在於它們的使用方式:

  • functools.update_wrapper(wrapper, wrapped): 這是一個普通的函數,你可以明確地調用它,將包裝函數和被包裝的函數作為 arguments。它就地修改包裝器函數,並返回該函數。這個函數經常被用在 decorator 的定義中,以使 decorator 模仿被裝飾的函數。

  • @functools.wraps(wrapped): 這是一個 decorator,你在 decorator 內部定義包裝函數時使用。它簡化了functools.update_wrapper()的用法,允許你用 decorator 的語法將其包裝起來。

assignedupdated 定義了原始函數的哪些屬性被轉移到包裝函數中。

  • assigned 屬性被直接轉移。

  • updated 屬性在包裝函數的 dictionary 中被更新。

update_wrapper() 向引用原始函數的包裝函數加入 __wrapped__ 屬性。 這意味著如果您有一個包裝函數,您可以透過 __wrapped__ 屬性訪問原始函數。此屬性對於 introspection 或繞過 decorators (例如:@lru_cache) 特別有用。

Introspection:這是指在運行時,檢查物件屬性的過程。例如:物件的類型、屬性、方法...。

update_wrapper()可以用於任何可調用物件,不僅僅是函數。

如果原始物件缺少 assignedupdated 中列出的任何屬性,update_wrapper() 將忽略它們,並且不會嘗試在包裝函數中設置它們。

然而,如果包裝函數缺少 updated 中列出的任何屬性,這將引發一個AttributeError

範例:

包含的方法


functools.update_wrapper() 函數中,屬性 __module____name____qualname____doc____annotations__ 直接從原始函數複製到包裝函數。 原始函數的 __dict__ 屬性用於更新包裝函數的 __dict__ 屬性。 這有助於使包裝函數看起來更像原始函數。

在 Python 中,每個物件都有許多 built-in 屬性,讓我們了解一下這些屬性:

  • __module__:該屬性包含定義函數的 module 名稱。 如果函數定義在 main module 中,則 __module__ 屬性為 __main__

  • __name__:該屬性包含函數的原始名稱。

  • __qualname__:該屬性是 "qualified name" 的縮寫,包括函數名稱、任何封閉 Class 。 它有助於明確地識別函數或 Class。

  • __doc__:該屬性包含函數的 docstring,它是為函數提供文檔的 string。

  • __annotations__:該屬性是一個字典,包含函數參數或返回值的所有註釋。

  • __dict__:該屬性是一個字典,包含函數的名稱空間,即其屬性名稱和值。

執行結果:

範例 – 略過 (不調用) Decorator


假設您出於某種原因想要繞過 (不調用) decorator , 您可以透過調用 __wrapped__ 屬性來做到這一點(例如:@lru_cache)。該技術的實際用途,取決於您正在使用的特定 decorators 和函數。

Bypassing the decorator:

範例 – @wraps() vs update_wrapper()


partial()


允許您建立一個帶有一些預設參數的新函數,我們只需要帶入剩下所需的參數即可。這在具有許多參數的函數或建立函數的變體,以供特定用途時非常有用。(例如:修復函數、隱藏函數參數建立 API)

  • func:建立其 partial 函數的原始函數。

  • args:partial 函數的 positional arguments。

  • keywords:partial 函數的 keyword arguments。

partial() 並不會實際運行自己本身。 在調用時,將使用預定參數運行原始函數。

partial() 大致等效以下程式碼:

範例 – 基礎


範例 – 帶參數的 Decorators


Decorators 透過將函數作為參數,並返回一個新函數來工作,該新函數通常會擴展或更改輸入函數的行為。 因此,Decorators 通常被設計為只接受一個 argument:被裝飾的函數

當您想要建立一個接受自己的參數的 decorator 時 (除了它所裝飾的函數之外),常見的做法是創建一個 Decorator factory (返回裝飾器的函數) 或是使用 partial() 做為 decorator factory。

範例:使用 Decorator factory

範例:使用 partial()

partialmethod()


用於 "凍結" 方法的某些參數,生成固定指定參數的新可調用物件。 它可以被視為 partial() 的方法版本,只不過它被設計為用作定義,而不能直接可調用。

  • func:凍結某些參數的方法,即調用的方法。

  • *args:傳遞給 func 的 positional arguments。

  • **keywords:傳遞給 func 的 keyword arguments。

該函數返回一個新的 partial 方法描述符,當從實例訪問時,其行為類似於屬性。 partial 方法將提供的 args, keywords 綁定到調用的方法。

partialmethod()不知道也不關心 decorators —— 它只是包裝您提供給它的函數。 這意味著您可以將 partialmethod 與 decorators 一起使用。

範例:

Last updated

Was this helpful?