Python 里的所有双下划线(dunder)方法、函数和属性

Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
15 min. read Python 3.8—3.12

你刚刚创建了一个类。你创建了一个 __init__ 方法。现在怎么办?

Python 包含大量的 dunder 方法("双下划线 "方法),它们允许我们深度定制我们的自定义类与 Python 的许多特性交互的方式。您可以为您的类添加哪些 dunder 双下划线方法,使它对使用它的其他 Python 程序员友好?

让我们来看看 Python 中的各种种 dunder 双下划线方法,重点是每种方法在什么情况下有用。

请注意,Python 文档将这些方法称为特殊方法,并指出了同义词 "magic method",但很少使用 "dunder method "一词。然而,"dunder 双下划线方法 "是一个相当常见的 Python 口语,正如我的非官方 Python 词汇表所指出的。

您可以使用散布在本页中的链接来获取任何特定 dunder 双下划线方法的详细信息。有关所有 dunder 双下划线方法的列表,请参阅最后一节中的小抄。

3 个基本的双下划线方法 🔑

大多数类都应该有 3 个 dunder 方法:__init____repr____eq__

程序 Dunder 方法调用 返回
T(a, b=3) T.__init__(x, a, b=3) None
repr(x) x.__repr__() str
x == y x.__eq__(y) Typically bool

__init__ 方法是初始化器(不要与构造函数混淆),__repr__ 方法自定义对象的字符串表示形式,__eq__ 方法自定义对象相等的含义。

__repr__ 方法在 Python REPL 和调试时特别有用。

相等比较和 hash 🟰

除了 __eq__ 方法外,Python 还有另外两个 dunder 方法,用于确定对象相对于其他对象的 "值"。

程序 Dunder 方法调用 返回
x == y x.__eq__(y) Typically bool
x != y x.__ne__(y) Typically bool
hash(x) x.__hash__() int

Python 的 __eq__ 方法通常返回 TrueFalseNotImplemented(如果对象无法比较)。缺省的 __eq__ 实现依赖于 is 操作符,它会检查同一性。

__ne__ 的默认实现调用 __eq__ 并否定给出的布尔返回值(如果 __eq__ 否定了布尔返回值,则返回 NotImplemented)。这种默认行为通常 "足够好",所以你几乎看不到__ne__的实现。

Hashable 对象可以用作字典中的键或集合中的值。Python 中的所有对象默认都是可散列的,但如果您编写了自定义的 __eq__ 方法,那么如果没有自定义的 __hash__ 方法,您的对象就不是可散列的。但是对象的哈希值必须永不改变,否则就会发生不好的事情,所以通常只有不可变对象才会实现 __hash__

要实现相等检查,请参见 Python 中的 __eq__ 。要实

现散列性,请参见在 Python 中制作散列对象。

不等比较 ⚖️

Python 的比较运算符 (<, >, <=, >=) 也都可以用 dunder 方法重载。比较运算符也为依赖于对象相对排序的函数提供了动力,如 sortedminmax

程序 Dunder 方法调用 返回
< __lt__ Typically bool
> __gt__ Typically bool
<= __le__ Typically bool
>= __ge__ Typically bool

如果您打算以典型的方式实现所有这些操作符(其中 x < y 与要求 y > x 相同),那么 Python 的 functools 模块中的 total_ordering decorator 就会派上用场。

类型转换和字符串格式化 ⚗️

Python 有许多将对象转换为不同类型的 dunder 方法。

函数 Dunder 方法调用 返回
str(x) x.__str__() str
bool(x) x.__bool__() bool
int(x) x.__int__() int
float(x) x.__float__() float
bytes(x) x.__bytes__() bytes
complex(x) x.__complex__() complex
f"{x:s}" x.__format__(s) str
repr(x) x.__repr__() str

__bool__ 函数用于真实性检查,但 __len__ 可作为备用。

如果需要创建一个类似于数字的对象(如 decimal.Decimalfractions.Fraction),就需要实现 __int__, __float____complex__ 函数,以便将对象转换为其他数字。如果你想创建一个可以在内存视图中使用或可以转换为字节的对象,你需要一个 __bytes__ 方法。

__format____repr__ 方法是不同的字符串转换方法。大多数字符串转换依赖于 __str__ 方法,但默认的 __str__ 实现只是简单地调用 __repr__ 方法。

所有 f-string 转换、str 类的 format 方法和内置 format 函数(很少使用)都使用 __format__ 方法。该方法允许日期时间对象支持自定义格式规范。

上下文管理器 🚪

上下文管理器是可以在 with 块中使用的对象。

使用 Dunder 方法调用 返回
with block enter x.__enter__() A value given to as
with block exit x.__exit__(exc_type, exc, traceback) Truthy/falsey value

有关上下文管理器的更多信息,请参阅 "什么是上下文管理器 "和 "创建上下文管理器"。

容器 和 集合collections 🗃️

Collections(又称容器)本质上是数据结构或类似数据结构的对象。Lists, dictionaries, sets, strings和元组tuples都是集合collections的例子。

程序 Dunder 方法调用 Return Type Implemented
len(x) x.__len__() integer Very common
iter(x) x.__iter__() iterator Very common
for item in x: ... x.__iter__() iterator Very common
x[a] x.__getitem__(a) any object Common
x[a] = b x.__setitem__(a, b) None Common
del x[a] x.__delitem__(a) None Common
a in x x.__contains__(a) bool Common
reversed(x) x.__reversed__() iterator Common
next(x) x.__next__() any object Uncommon
x[a] x.__missing__(a) any object Uncommon
operator.length_hint(x) x.__length_hint__() integer Uncommon

__iter__ 方法用于 iter 函数和所有形式的迭代:for 循环、comprehensions、元组解包,以及使用 * 进行可迭代解包。

__iter__ 方法是创建自定义可迭代器所必需的,而 __next__ 方法则是创建自定义迭代器所必需的(这种情况比较少见)。除非其他类决定实现 __missing__ 方法,否则 __missing__ 方法只会被 dict 类自身调用。__length_hint__ 方法为不支持 __len__ 的结构提供一个长度猜测,以便更有效地预设列表或其他结构的大小。

另请参见:迭代器协议、实现 __len__ 和实现 __getitem__

调用 ☎️

函数、类和所有其他可调用对象都依赖于 __call__ 方法。

程序 Dunder 方法调用 Return Type
x(a, b=c) x.__call__(a, b=c) any object

调用类时,会使用其元类metaclass__call__ 方法。调用类实例时,使用该类的 __call__ 方法。

有关可调用性的更多信息,请参见 Callables:Python 的 "函数 "有时也是类。

算术运算符 ➗

Python 的 dunder 方法经常被描述为 "运算符重载 "的工具。这种 "运算符重载 "大多以 Python 各种算术运算符的形式出现。

有两种方法可以分解算术运算符:

  • 数学运算符 (e.g. +, -, *, /, %) 与位运算符 (e.g. &, |, ^, >>, ~)
  • 二进制运算符(在 2 个数值之间,如 x + y) 与一元运算符(在 1 个数值之前,如 +x)

数学运算符比位运算符更常见,二进制运算符比一元运算符更常见。

这些就是二进制数学算术运算符:

程序 Left-Hand Method Right-Hand Method Description
x + y __add__ __radd__ Add / Concatenate
x - y __sub__ __rsub__ Subtract
x * y __mul__ __rmul__ Multiply
x / y __truediv__ __rtruediv__ Divide
% __mod__ __rmod__ Modulo
x // y __floordiv__ __rfloordiv__ Integer division
** __pow__ __rpow__ Exponentiate
x @ y __matmul__ __rmatmul__ Matrix multiply

每个运算符都包括左手和右手方法。如果 x.__add__(y) 返回 NotImplemented,那么将尝试 y.__radd__(x)。有关更多信息,请参见算术 Dunder 方法。

这些是二进制位运算符:

程序 Left-Hand Method Right-Hand Method Description
x & y __and__ __rand__ AND
x | y __or__ __ror__ OR
x ^ y __xor__ __rxor__ XOR
x >> y __rshift__ __rrshift__ Right-shift
x << y __lshift__ __rlshift__ Left-shift

这些是 Python 的一元算术运算符:

程序 Dunder Method Variety Description
-x __neg__ Mathematical Negate
+x __pos__ Bitwise Affirm
~x __invert__ Bitwise Invert

一元 + 运算符通常没有任何作用,但有些对象会将其用于特定操作。例如,在 collections.Counter 对象上使用 + 会删除非正值。

Python 的算术运算符经常用于非算术目的:序列使用 + 来连接,* 来自连;集合使用 & 来交集,| 来联合,- 来非对称差值,^ 来对称差值。算术运算符有时也会被重载,用于更有创意的用途。例如,pathlib.Path 对象使用 / 来创建子路径

In-place 算术运算 ♻️

Python 包含许多用于 In-place 运算的 dunder 方法。如果您要创建一个支持任何算术运算的可变对象,您也需要实现相关的In-place dunder 方法。

程序 Dunder 方法调用 返回
x += y x.__iadd__(y) Typically self
x -= y x.__isub__(y) Typically self
x *= y x.__imul__(y) Typically self
x /= y x.__itruediv__(y) Typically self
x %= y x.__imod__(y) Typically self
x //= y x.__ifloordiv__(y) Typically self
x **= y x.__ipow__(y) Typically self
x @= y x.__imatmul__(y) Typically self
x &= y x.__iand__(y) Typically self
x |= y x.__ior__(y) Typically self
x ^= y x.__ixor__(y) Typically self
x >>= y x.__irshift__(y) Typically self
x <<= y x.__ilshift__(y) Typically self

Python 的所有二进制算术运算符都可以在增强赋值语句中使用,即在对一个对象执行操作的同时,使用一个运算符和 = 符号对该对象进行赋值。

由于可变对象为 in-place 运算实现了适当的 dunder 方法,因此对可变对象的增强赋值预计会对原始对象进行变异。

如果找不到用于 in-place 运算的 dunder 方法,Python 会在执行运算后进行赋值。不可变对象通常不实现 in-place 运算的 dunder 方法,因为它们应该返回一个新对象,而不是改变原来的对象。

内置数学函数 🧮

Python 还为许多数学相关函数提供了 dunder 方法,包括内置函数和数学库中的一些函数。

程序 Dunder 方法调用 返回
divmod(x, y) x.__divmod__(y) 2-item tuple
divmod(x, y) y.__rdivmod__(x) 2-item tuple
abs(x) x.__abs__() float
sequence[x] x.__index__() int
round(x) x.__round__() Number
math.trunc(x) x.__trunc__() Number
math.floor(x) x.__floor__() Number
math.ceil(x) x.__ceil__() Number

Python 的 divmod 函数同时执行整除 (//) 和模数运算 (%)。请注意,就像许多二进制算术运算符一样,如果 divmod 需要询问第二个参数来处理操作,它也会检查 __rvidmod__ 方法。

__index__ 方法用于制作类整数对象。该方法可以无损地转换为整数,而不像 __int__ 可以进行 "有损 "的整数转换(例如从 float 转换为 int)。它用于需要真正整数的操作,如切片、索引以及 binhexoct 函数(example)。

属性访问 📜

Python 甚至包含了控制访问、删除或赋值对象上的任何属性时会发生什么的 dunder 方法!

程序 Dunder 方法调用 返回
x.missing x.__getattr__("missing") Attribute value
x.anything x.__getattribute__("anything") Attribute value
x.thing = value x.__setattr__("thing", value) None
del x.thing x.__delattr__("thing") None
dir(x) x.__dir__() List of strings

每次属性访问都会调用 __getattribute__ 方法,而 __getattr__ 方法只有在 Python 找不到给定属性时才会被调用。所有的方法调用和属性访问都会调用 __getattribute__ 方法,因此正确地实现它是一个挑战(由于意外的递归)。

__dir__ 方法应返回一个属性名可迭代器(字符串)。当 dir 函数调用 __dir__ 时,它会把返回的可迭代项转换成一个排序列表(就像 sorted 所做的那样)。

内置的 getattrsetattrdelattr 函数与同名的 dunder 方法相对应,但它们只用于动态属性访问(而不是所有属性访问)。

元编程 🪄

现在我们开始使用真正不寻常的 dunder 方法。Python 包含许多用于元编程相关特性的 dunder 方法。

Implemented on 程序 Dunder 方法调用 返回
Metaclasses class T: ... type(base).__prepare__() mapping
Metaclasses isinstance(x, T) T.__instancecheck__(x) bool
Metaclasses issubclass(U, T) T.__subclasscheck__(U) bool
Any class class U(T): ... T.__init_subclass__(U) None
Any class (Called manually) T.__subclasses__() list
Any class class U(x): ... x.__mro_entries__([x]) tuple
Any class T[y] T.__class_getitem__(y) an item

__prepare__ 方法自定义用于类的初始命名空间的字典。该方法用于预先填充字典值或自定义字典类型(愚蠢的例子)。

__instancecheck____subclasscheck__ 方法覆盖了 isinstanceissubclass 的功能。Python 的 ABC 使用这些方法来练习 goose typing(在类型检查时进行鸭式键入)。

__init_subclass__ 方法允许类挂钩子类初始化 (示例)。类也有一个 __subclasses__ 方法 (在它们的元类 metaclass上),但通常不会被重载。

Python 在类继承过程中会调用 __mro_entries__ 方法来处理实际上不是类的基类。typing.NamedTuple 函数使用它来假装它是一个类 (参见此处)。

__class_getitem__ 方法允许类可下标(其元类不需要 __getitem__ 方法)。它通常用于启用花哨的类型注解(如 list[int])。

描述符 Descriptors 🏷️

Descriptors描述符是一种对象,当附加到一个类时,可以挂钩访问该类上所附加的属性名。

程序 Dunder 方法调用 返回
class T: x = U() T.x.__set_name__(T, 'x') None
t.x T.x.__get__(t, T) The value
t.x = y T.x.__set__(t, y) None
del t.x T.x.__delete__(t) None

描述符协议主要是为了让 Python 的属性装饰器工作而存在的一个特性,尽管它也被许多第三方库所使用。

Buffers 💾

要实现一个低级的内存数组?您需要 Python 的buffer 协议

程序 Dunder 方法调用 返回
memoryview(x) x.__buffer__(flags) memoryview
del memoryview(x) x.__release_buffer__(m) None

当删除从 __buffer__ 返回的 buffer 时,会调用 __release_buffer__ 方法。

Python 的缓冲区协议通常用 C 语言实现,因为它针对的是低级对象。

异步操作 🤹

想要实现异步上下文管理器?您需要这些 dunder 方法:

  • __aenter__: 与 __enter__, 类似,但它返回一个 awaitable 对象
  • __aexit__: 一样 __exit__, 但它返回一个 awaitable 对象

需要支持异步迭代?你需要这些 dunder 方法:

  • __aiter__: 必须返回异步迭代器 iterator
  • __anext__: like __next__ 类似于__next__或非异步迭代器,但它必须返回一个可 awaitable 对象,而且应该引发 StopAsyncIteration 而不是 StopIteration

需要创建自己的 awaitable 对象吗?你需要这个 dunder 方法:

  • __await__: 返回一个迭代器 iterator

我在自定义异步对象方面经验不多,所以请到别处了解更多详情。

构建 Construction 和 finalizing 🏭

最后几个 dunder 方法与对象的创建和销毁有关。

程序 Dunder 方法调用 返回
T(a, b=3) T.__new__(T, a, b=3) New instance ( x)
T(a, b=3) T.__init__(x, a, b=3) None
del x x.__del__() None

调用类会返回一个新的类实例,这要归功于 __new__ 方法。__new__ 方法是 Python 的构造函数方法,不过与许多编程语言中的构造函数不同,您几乎不应该定义自己的 __new__ 方法。要控制对象的创建,应首选初始化器 (__init__),而不是构造函数 (__new__)。下面是一个奇怪的 __new__ 例子。

你可以把 __del__ 想象成一个 "析构 destructor"方法,尽管它实际上被称为 finalizer 方法。在一个对象被删除之前,它的 __del__ 方法会被调用(示例)。文件实现了一个 __del__ 方法,用于关闭文件和它可能链接到的任何二进制文件缓冲区。

特定库的 dunder 方法 🧰

一些标准库模块定义了自定义的 dunder 方法,这些方法在其他地方没有使用:

  • dataclasses 支持一个 __post_init__ 方法
  • abc.ABC 类有一个 __subclasshook__ 方法, abc.ABCMeta 在其 __subclasscheck__ 方法中调用了该方法(更多内容请参见 goose typing)
  • Path-like 对象有一个 __fspath__ 方法,它以字符串形式返回文件路径
  • Python 的 copy 将使用 __copy____deepcopy__ 方法,如果有的话。
  • Pickling 依赖于 __getnewargs_ex____getargs__, 尽管 __getstate____setstate__ 可以进一步自定义,而 __reduce____reduce_ex__ 则是更低级的方法
  • sys.getsizeof 依靠 __sizeof__ 方法来获取对象的大小(以字节为单位)

Dunder 属性 📇

除 Dunder 方法外,Python 还有许多非方法的 Dunder 属性。

下面是一些比较常见的 dunder 属性:

  • __name__: name of a function, classes, or module
  • __module__: module name for a function or class
  • __doc__: docstring for a function, class, or module
  • __class__: an object's class (call Python's type function instead)
  • __dict__: most objects store their attributes here (see where are attributes stored?)
  • __slots__: classes using this are more memory efficient than classes using __dict__
  • __match_args__: classes can define a tuple noting the significance of positional attributes when the class is used in structural pattern matching ( match- case)
  • __mro__: a class's method resolution order used when for attribute lookups and super() calls
  • __bases__: the direct parent classes of a class
  • __file__: the file that defined the module object (though not always present!)
  • __wrapped__: functions decorated with functools.wraps use this to point to the original function
  • __version__: commonly used for noting the version of a package
  • __all__: modules can use this to customize the behavior of from my_module import *
  • __debug__: running Python with -O sets this to False and disables Python's assert statements

这些只是比较常见的 Dunder 属性。下面还有一些: Here are some more:

  • Functions have __defaults__, __kwdefaults__, __code__, __globals__, and __closure__
  • Both functions and classes have __qualname__, __annotations__, and __type_params__
  • Instance methods have __func__ and __self__
  • Modules may also have __loader__, __package__, __spec__, and __cached__ attributes
  • Packages have a __path__ attribute
  • Exceptions have __traceback__, __notes__, __context__, __cause__, and __suppress_context__
  • Descriptors use __objclass__
  • Metaclasses use __classcell__
  • Python's weakref module uses __weakref__
  • Generic aliases have __origin__, __args__, __parameters__, and __unpacked__
  • The sys module has __stdout__ and __stderr__ which point to the original stdout and stderr versions

此外,各种标准库模块也使用这些 dunder 属性:__covariant____contravariant____infer_variance____bound____constraints__。Python 有一个内置的 __import__ 函数,但你不应该使用它(最好使用 importlib.import_module),CPython 有一个 __builtins__ 变量,它指向 buildins 模块(但这是一个实现细节,需要时应显式导入 buildins)。另外,从 __future__ 模块导入可以启用特定的 Python 功能标志,Python 会在软件包中查找 __main__ 模块,使它们可以作为 CLI 脚本运行。

这只是你在 Python 中能找到的大部分 dunder 属性名 😵

所有 Python dunder 双下划线方法:一个速查表 ⭐

这是所有 Python dunder 方法的分类,大致按照最常见的方法排序。下面指出了一些注意事项。

Category 程序 Dunder 方法调用 返回
Object Creation x = T(a, b) x.__init__(a, b) None
Object Creation x = T(a, b) T.__new__(T, a, b) New instance ( x)
Finalizer del x (ish) x.__del__() None
Comparisons x == y x.__eq__(y) Typically bool
Comparisons x != y x.__ne__(y) Typically bool
Comparisons x < y x.__lt__(y) Typically bool
Comparisons x > y x.__rt__(y) Typically bool
Comparisons x <= y x.__le__(y) Typically bool
Comparisons x >= y x.__ge__(y) Typically bool
Hashability hash(x) x.__hash__() int
Conversions repr(x) x.__repr__() Always str
Conversions str(x) x.__str__() Always str
Conversions bool(x) x.__bool__() Always bool
Conversions int(x) x.__int__() Always int
Conversions float(x) x.__float__() Always float
Conversions bytes(x) x.__bytes__() Always bytes
Conversions complex(x) x.__complex__() Always complex
Conversions format(x, s) x.__format__(s) Always str
Context Managers with x as c: x.__enter__() The c object
Context Managers with x as c: x.__exit__() Truthy/falsey value
Collections len(x) x.__len__() int
Collections iter(x) x.__iter__() An iterator
Collections x[a] x.__getitem__(a)
Collections x[a] = b x.__setitem__(a, b) None
Collections del x[a] x.__delitem__(a) None
Collections a in x x.__contains__(a) bool
Collections reversed(x) x.__reversed__() An iterator
Collections next(x) x.__next__() Next iterator item
Collections x[a] x.__missing__(a)
Collections x.__length_hint__() int
Arithmetic x + y x.__add__(y)
Arithmetic x + y y.__radd__(x)
Arithmetic x - y x.__sub__(y)
Arithmetic x - y y.__rsub__(x)
Arithmetic x * y x.__mul__(y)
Arithmetic x * y y.__rmul__(x)
Arithmetic x / y x.__truediv__(y)
Arithmetic x / y y.__rtruediv__(x)
Arithmetic x % y x.__mod__(y)
Arithmetic x % y y.__rmod__(x)
Arithmetic x // y x.__floordiv__(y)
Arithmetic x // y y.__rfloordiv__(x)
Arithmetic x ** y x.__pow__(y)
Arithmetic x ** y y.__rpow__(x)
Arithmetic x @ y x.__matmul__(y)
Arithmetic x @ y y.__rmatmul__(x)
Arithmetic x & y x.__and__(y)
Arithmetic x & y y.__rand__(x)
Arithmetic x | y x.__or__(y)
Arithmetic x | y y.__ror__(x)
Arithmetic x ^ y x.__xor__(y)
Arithmetic x ^ y y.__rxor__(x)
Arithmetic x >> y x.__rshift__(y)
Arithmetic x >> y y.__rrshift__(x)
Arithmetic x << y x.__lshift__(y)
Arithmetic x << y y.__rlshift__(x)
Arithmetic -x x.__neg__()
Arithmetic +x x.__pos__()
Arithmetic ~x x.__invert__()
Math functions divmod(x, y) x.__divmod__(y) 2-item tuple
Math functions abs(x) x.__abs__() float
Math functions x.__index__() int
Math functions round(x) x.__round__() Number
Math functions math.trunc(x) x.__trunc__() Number
Math functions math.floor(x) x.__floor__() Number
Math functions math.ceil(x) x.__ceil__() Number
Assignment x += y x.__iadd__(y) Typically self
Assignment x -= y x.__isub__(y) Typically self
Assignment x *= y x.__imul__(y) Typically self
Assignment x /= y x.__itruediv__(y) Typically self
Assignment x %= y x.__imod__(y) Typically self
Assignment x //= y x.__ifloordiv__(y) Typically self
Assignment x **= y x.__ipow__(y) Typically self
Assignment x @= y x.__imatmul__(y) Typically self
Assignment x &= y x.__iand__(y) Typically self
Assignment x |= y x.__ior__(y) Typically self
Assignment x ^= y x.__ixor__(y) Typically self
Assignment x >>= y x.__irshift__(y) Typically self
Assignment x <<= y x.__ilshift__(y) Typically self
Attributes x.y x.__getattribute__('y')
Attributes x.y x.__getattr__('y')
Attributes x.y = z x.__setattr__('y', z) None
Attributes del x.y x.__delattr__('y') None
Attributes dir(x) x.__dir__() An iterable
Descriptors class T: x = U() T.x.__set_name__(T, 'x') None
Descriptors t.x T.x.__get__(t, T)
Descriptors t.x = y T.x.__set__(t, y) None
Descriptors del t.x T.x.__delete__(t) None
Class stuff class U(T): ... T.__init_subclass__(U) None
Class stuff class U(x): ... x.__mro_entries__([x]) tuple
Class stuff T[y] T.__class_getitem__(y)
Metaclasses class T: ... type(base).__prepare__() dict/mapping
Metaclasses isinstance(x, T) T.__instancecheck__(x) bool
Metaclasses issubclass(U, T) T.__subclasscheck__(U) bool
Async await x (ish) x.__await__() An iterator
Async async with x: x.__aenter__() An awaitable
Async async with x: x.__aexit__() An awaitable
Async async for a in x: x.__aiter__() An awaitable
Async async for a in x: x.__anext__() An awaitable
Buffers memoryview(x) x.__buffer__(flags) memoryview
Buffers del memoryview(x) x.__release_buffer__(m) None

上表有一个轻微但一致的错误。大多数 dunder 方法实际上不是直接在对象上调用的,而是在对象的类型上调用的:type(x).__add__(x, y) 而不是 x.__add__(y)。这种区别在元类方法中最为重要。

我还特意排除了特定于库的 dunder 方法(如 __post_init__)和你不可能定义的 dunder 方法(如 __subclasses__ )。请参见下文。

Category 程序 Dunder 方法调用 返回
Dataclasses x = T(a, b) T.__post_init__(a, b) None
Copying copy.copy(x) x.__copy__() New object
Copying copy.deepcopy(x) x.__deepcopy__(memo) New object
Pickling pickle.dumps(x) x.__getnewargs__() A 2-item tuple
Pickling pickle.dumps(x) x.__getnewargs_ex__() A 2-item tuple
Pickling pickle.dumps(x) x.__getstate__() A meaningful state
Pickling pickle.dumps(x) x.__reduce__() A 2-6 item tuple
Pickling pickle.dumps(x) x.__reduce_ex__(4) A 2-6 item tuple
Pickling pickle.loads(b) x.__setstate__(state) None
pathlib os.fspath(x) p.__fspath__() str or bytes
sys sys.getsizeof(x) x.__sizeof__() int (size in bytes)
Class stuff None? x.__subclasses__() Subclasses iterable
ABCs issubclass(U, T) T.__subclasshook__(U) bool

因此,Python 包含 103 个 "普通 "的 dunder 方法,12 个特定于库的 dunder 方法,以及至少 52 个不同类型的其他 dunder 属性。这就是超过 150 个独特的 __dunder__ 名称!我不建议记住这些名称:让 Python 完成它的工作,并随时查找您需要实现/查找的 dunder 方法或属性。

请记住,你并不是要发明你自己的 dunder 双下划线方法。有时您会看到一些第三方库发明了自己的 dunder 双下划线方法,但我们并不鼓励这样做,而且如果用户遇到这样的方法并认为它们是 "真正的 "dunder 双下划线方法,他们会感到非常困惑。