Python中下劃線用法

banq發表於2024-06-03

"Dunder" 是 "double underscore" 的一個俚語縮寫,它通常用來指代 Python 中以雙下劃線(__)開頭和結尾的變數、方法或屬性。在 Python 中,這些雙下劃線包圍的名稱被稱為 "魔法方法"(magic methods)或 "特殊方法"(special methods),它們有特殊的意義和用途。

這些方法允許開發者定義或改變類的預設行為,使得自定義類可以與 Python 的內建型別一樣工作。例如,__init__ 是一個初始化方法,當一個例項被建立時會自動呼叫;__repr__ 方法則定義了物件的官方字串表示形式,通常在除錯時非常有用。

以下是一些常見的 dunder 方法示例:

  • __init__(self, ...): 類的構造器,用於初始化新建立的物件。
  • __repr__(self): 當使用 repr() 函式或內建的 print() 函式時,返回物件的字串表示。
  • __str__(self): 當使用 str() 函式或在需要字串的上下文中使用物件時,返回物件的字串表示。
  • __eq__(self, other): 定義物件的等價性,用於比較兩個物件是否相等。
  • __lt__, __le__, __gt__, __ge__: 定義物件的大小比較關係。
  • __add__(self, other): 定義加法行為,例如 x + y。
  • __call__(self, ...): 允許一個類的例項像函式那樣被呼叫。

Dunder 方法和屬性是 Python 物件導向程式設計的強大工具,它們使得 Python 的類可以高度定製化,以適應各種程式設計需求。

三種基本的 Dunder 方法

  • __init__:初始化物件,是初始化程式(不要與建構函式混淆)
  • __repr__:提供字串表示形式
  • __eq__:定義相等性

等同性:
__ne__和__hash__之類的方法用於值比較以及確定物件是否可以用作字典鍵或集合元素。

  • x.__eq__(y) 類似 x == y :Python 的__eq__方法通常返回True、False或NotImplemented(如果物件無法比較)。預設__eq__實現依賴於is運算子,用於檢查身份。
  • x.__ne__(y) 類似x != y:__ne__呼叫__eq__並否定任何給定的布林返回值
  • x.__hash__()類似hash(x):可用作字典中的鍵或集合中的值。Python 中的所有物件都是可雜湊的,但如果您編寫了自定義方法,則沒有自定義方法,您的物件將無法雜湊__hash__。通常只有不可變物件才能實現__hash__

可排序性:
可以使用 <、、。><=>=等方法過載比較運算子,例如__lt____gt____le____ge__

型別轉換和字串格式化:
Python 有許多 dunder 方法用於將物件轉換為不同型別。

如果您需要建立一個像數字一樣的物件(例如decimal.Decimal或fractions.Fraction),則需要實現__int__、__float__,__complex__
這樣您的物件就可以轉換為其他數字。

如果您想建立一個可以在 中使用memoryview或可以轉換為 的物件bytes,則需要一種__bytes__方法。

__format__ 和 __repr__ 方法是不同的字串轉換方法。

大多數字符串轉換都依賴 __str__ 方法,但預設的 __str__ 實現只是簡單地呼叫 __repr__。

所有 f-string 轉換、str 類的 format 方法以及內建 format 函式(很少使用)都使用 __format__ 方法。該方法允許日期時間物件支援自定義格式規範。

上下文管理器使用
__enter__ 和 __exit__ 方法實現上下文管理器,可以將 with 語句用於自定義物件。

with block enter    x.__enter__()   
with block exit    x.__exit__(exc_type, exc, traceback)    

屬性訪問 
Python 甚至包含了控制訪問、刪除或賦值物件上的任何屬性時會發生什麼的 dunder 方法!

x.missing    x.__getattr__(<font>"missing")    
x.anything    x.__getattribute__(
"anything")    
x.thing = value    x.__setattr__(
"thing", value)  
del x.thing    x.__delattr__(
"thing")    
dir(x)    x.__dir__()

每次屬性訪問都會呼叫 __getattribute__ 方法,而 __getattr__ 方法只有在 Python 找不到給定屬性時才會被呼叫。所有的方法呼叫和屬性訪問都會呼叫 __getattribute__ 方法,因此正確地實現它是很有挑戰性的(由於意外的遞迴)。請參見 __getattr__ 與 __getattribute__ 以瞭解演示這兩種方法之間區別的示例。

__dir__ 方法應返回一個屬性名可迭代器(字串)。當 dir 函式呼叫 __dir__ 時,它會把返回的可迭代項轉換成一個排序列表(就像 sorted 所做的那樣)。

內建的 getattr、setattr 和 delattr 函式與同名的 dunder 方法相對應,但它們只用於動態屬性訪問(而不是所有屬性訪問)。

描述符 
描述符是一種物件,當它附加到一個類上時,可以掛鉤訪問該類上所附加的屬性名。

class T: x = U()    T.x.__set_name__(T, 'x')    
t.x    T.x.__get__(t, T)  
t.x = y    T.x.__set__(t, y)  
del t.x    T.x.__delete__(t)  

描述符協議主要是為了讓 Python 的屬性裝飾器工作而存在的一個特性,儘管它也被許多第三方庫所使用。

非同步操作
想要實現非同步上下文管理器?您需要這些 dunder 方法:

  • __aenter__:與 __enter__ 類似,但它返回一個等待物件
  • __aexit__:就像 __exit__ 一樣,但它返回一個 awaitable 物件

需要支援非同步迭代?你需要這些 dunder 方法:
  • __aiter__:必須返回非同步迭代器
  • __anext__:類似於__next__或非非同步迭代器,但它必須返回一個可等待物件,而且應該引發 StopAsyncIteration 而不是 StopIteration。
  •  

需要建立自己的可等待物件嗎?你需要這個 dunder 方法:
  • __await__:返回一個迭代器

Dunder 屬性
除了 Dunder 方法,Python 還有許多非方法的 Dunder 屬性。

下面是您會看到的一些更常見的 dunder 屬性:

  • __name__:函式、類或模組的名稱
  • __module__:函式或類的模組名
  • __doc__:函式、類或模組的 docstring
  • __class__:物件的類(可呼叫 Python 的型別函式來代替)
  • __dict__:大多數物件在這裡儲存屬性(參見屬性儲存在哪裡?)
  • __slots__:使用它的類比使用 __dict__ 的類更節省記憶體
  • __match_args__:當類用於結構模式匹配(match-case)時,類可以定義一個元組,指出位置屬性的重要性。
  • __mro__:在屬性查詢和 super() 呼叫時使用類的方法解析順序
  • __bases__:類的直接父類
  • __file__:定義模組物件的檔案(雖然不一定存在!)。
  • __wrapped__:用 functools.wraps 修飾的函式,用它來指向原始函式
  • __version__:常用於標註軟體包的版本
  • __all__:模組可以用它來定製 from my_module import * 的行為。
  • __debug__:執行 Python 時使用 -O 將此設定為 False,並禁用 Python 的斷言語句


這些只是比較常見的 Dunder 屬性。下面還有一些:

  • 函式有__defaults__、__kwdefaults__、__code__、__globals__和__closure__。
  • 函式和類都有 __qualname__、__annotations__ 和 __type_params__
  • 例項方法有 __func__ 和 __self__
  • 模組也可能有__loader__、__package__、__spec__和__cached__屬性
  • 包有 __path__ 屬性
  • 異常有__traceback__、__notes__、__context__、__cause__和__suppress_context__。
  • 描述符使用 __objclass__
  • 元類使用 __classcell__
  • Python 的 weakref 模組使用 __weakref__
  • 通用別名有__origin__、__args__、__parameters__和__unpacked__。
  • sys 模組有 __stdout__ 和 __stderr__ 指向原始的 stdout 和 stderr 版本

因此,Python 包含 103 個 "普通 "的 dunder 方法,12 個特定於庫的 dunder 方法,以及至少 52 個不同型別的其他 dunder 屬性。這就是超過 150 個獨特的 __dunder__ 名稱!

不建議記住這些名稱:讓 Python 完成它的工作,並隨時查詢您需要實現/查詢的 dunder 方法或屬性。

請記住,你並不是要發明你自己的 dunder 方法。有時您會看到一些第三方庫發明了自己的 dunder 方法,但我們並不鼓勵這樣做,而且如果使用者遇到這樣的方法並認為它們是 "真正的 "dunder 方法,他們會感到非常困惑。
 

相關文章