这些方法允许开发者定义或改变类的默认行为,使得自定义类可以与 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 方法:
- 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
可排序性:
可以使用 <、、。><=>=等方法重载比较运算符,例如ltgtlege
类型转换和字符串格式化:
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.<strong>enter</strong>() |
属性访问
Python 甚至包含了控制访问、删除或赋值对象上的任何属性时会发生什么的 dunder 方法!
x.missing x.<strong>getattr</strong>("missing") |
每次属性访问都会调用 getattribute 方法,而 getattr 方法只有在 Python 找不到给定属性时才会被调用。所有的方法调用和属性访问都会调用 getattribute 方法,因此正确地实现它是很有挑战性的(由于意外的递归)。请参见 getattr 与 getattribute 以了解演示这两种方法之间区别的示例。
dir 方法应返回一个属性名可迭代器(字符串)。当 dir 函数调用 dir 时,它会把返回的可迭代项转换成一个排序列表(就像 sorted 所做的那样)。
内置的 getattr、setattr 和 delattr 函数与同名的 dunder 方法相对应,但它们只用于动态属性访问(而不是所有属性访问)。
描述符
描述符是一种对象,当它附加到一个类上时,可以挂钩访问该类上所附加的属性名。
class T: x = U() T.x.<strong>set_name</strong>(T, 'x') |
描述符协议主要是为了让 Python 的属性装饰器工作而存在的一个特性,尽管它也被许多第三方库所使用。
异步操作
想要实现异步上下文管理器?您需要这些 dunder 方法:
- aenter:与 enter 类似,但它返回一个等待对象
- aexit:就像 exit 一样,但它返回一个 awaitable 对象
- aiter:必须返回异步迭代器
- anext:类似于next或非异步迭代器,但它必须返回一个可等待对象,而且应该引发 StopAsyncIteration 而不是 StopIteration。
- 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 方法,他们会感到非常困惑。