跳至内容
Python

Python 双下划线方法:值得了解的特殊命令

了解 Python 的双下划线方法如何让自定义对象与内置操作自然协作,从打印到算术运算一应俱全。发现这些魔法方法如何简化类设计并解锁强大行为。
Oluseye Jeremiah's photo
Oluseye Jeremiah
已更新 · 8分钟 阅读

使用 AI 探索

ChatGPTClaudePerplexity

创建您的免费账户

实践并学习路径你的进步。

使用 Google 继续
继续操作即表示您接受我们的《使用条款》和《隐私政策》,并同意您的数据存储在美国。

您是否想过,为什么 Python 的自定义类能与内置函数无缝配合?这要归功于那些巧妙的“双下划线(dunder)”方法——即以双下划线开头和结尾的方法。

想象一下,您要创建一个表示复数的类,并希望自然地使用 + 运算符。与其编写额外的加法处理代码,不如定义一个 __add__ 方法,剩下的交给 Python。

这些魔法方法,如用于对象创建的 __init__ 和用于字符串表示的 __str__,都会被 Python 自动调用,您无需直接调用它们。它们既能避免与常规方法命名冲突,又为运算符重载和自定义行为提供动力。

无论是实例化对象、执行算术运算,还是简单地打印一个对象,双下划线方法都会在幕后悄然运作,让您的代码更优雅高效。如果您是 Python 新手,想快速补齐主要概念,建议先学习我们的Introduction to Python 课程,打好基础。

什么是 Python 中的双下划线方法?

正如前面提到的,Python 的双下划线方法是一些独特的“魔法”方法,通过让自定义类与 Python 的内置操作集成,构成了 Python 数据模型的骨架。这些操作包括打印、比较和执行算术等。

当您定义诸如用于初始化对象的 __init__、用于对象表示的 __repr__ 和 __str__,甚至用于清理的 __del__ 等方法时,本质上就是在告诉 Python 如何为您的对象处理标准操作。当执行相应操作时,解释器会自动调用这些方法。这种内置自动化在原生 Python 特性与用户自定义类之间架起了桥梁。

稍后我们将展示使用和不使用这些方法的类示例,帮助您直观理解其工作方式。

双下划线方法的类别

下面来看看双下划线方法的主要类别,以及它们如何增强您的代码能力。

1. 初始化与构造

像 __new__、__init__ 和 __del__ 这样的双下划线方法控制对象的创建、初始化以及最终销毁。

  • __new__ 与 __init__:__new__ 负责创建新实例,而 __init__ 负责对其进行初始化。在处理不可变类型时,这种分离尤为重要,此时您可能会为特定需求重写 __new__。

  • __del__:该方法充当析构函数,当对象即将被垃圾回收时,可用于清理。

您甚至可以在不可变类中自定义 __new__,以强制执行特定的创建规则。

2. 数值与算术方法

诸如 __add__、__sub__、__mul__ 及其原地变体的方法,让您的对象支持算术运算。这就是所谓的运算符重载。通过定义这些方法,您可以为自定义对象启用自然的算术行为。

例如,在 Vector 类中重载 __add__,以相加两个向量的对应元素。这在设计建模数学概念或金融工具的类时尤其有用。

3. 比较与相等性方法

像 __eq__、__lt__ 和 __gt__ 这样的方法决定对象的比较方式。这些方法可让您定义两个对象何为相等,或它们应如何排序。

一个典型示例是比较两个形状的面积:您可以重写 __lt__,当一个形状的面积小于另一个时返回 true。这在集合或排序算法中很有帮助。

4. 字符串表示方法

__str__ 和 __repr__ 方法控制对象以字符串形式的展示方式。

  • __repr__ 应提供对开发者友好的表示,可用于重建对象。

  • __str__ 侧重于对用户友好的展示。

5. 容器与可迭代方法

若要让对象表现得像序列或容器,您可以实现 __len__、__getitem__ 和 __iter__ 等方法。这将启用索引、迭代和成员测试等操作。

例如,如果您设计一个自定义栈或列表,实现这些方法后,就可以使用如 len() 这样的内置函数。

6. 可调用性与函数式编程

通过 __call__ 方法,您的类实例可以像函数一样被调用。这对于创建可缓存结果或在多次调用间保持内部状态的“有状态函数对象”尤其有用。您可以把它看作一个可反复以不同参数调用的小型计算引擎。

7. 上下文管理器

实现 __enter__ 和 __exit__ 方法,可让您的对象与语句配合进行资源管理。这对管理文件句柄或网络连接等资源至关重要,确保它们被正确设置与清理。

一个真实场景是使用自定义上下文管理器,安全地打开和关闭数据库连接。

8. 属性访问与描述符

诸如 __getattr__、__setattr__ 和 __delattr__ 等方法允许您控制属性的访问与修改。描述符协议则进一步细化,使对象能够动态管理属性访问。

例如,描述符可以在设置前校验或转换属性值,确保其满足特定条件。

进阶与其他双下划线方法  

除核心双下划线方法外,Python 还包含用于异步编程、元编程和特定库行为等任务的高级与专项方法。例如:  

  • 异步方法:__aiter__ 和 __anext__ 支持异步迭代,__await__ 则支持可等待对象。  

  • 元编程钩子:__prepare__ 和 __set_name__ 等方法允许动态创建类和命名属性。  

  • 特定库方法:诸如 NumPy 或 Pandas 等库常会定义自定义双下划线方法(例如用于 NumPy 集成的 __array__)。

这些高级方法通常用于专项任务,大多数开发者并不需要每天使用。不过,它们展现了 Python 的灵活性与深度。建议查阅官方 Python 数据模型文档 获取完整列表。该资源对所有双下划线方法及其预期用途均有详尽说明。

Python 中的所有双下划线方法

Python 提供了 100 多个双下划线方法,分别用于控制对象行为的不同方面。虽然本文已介绍关键类别,下面给出一份高层总结,供快速参考:

  • 算术方法: 如 __add__、__sub__、__mul__ 和 __truediv__ 使对象支持运算符(+、-、*、/)。

  • 比较方法: 如 __eq__、__lt__ 和 __gt__ 定义对象在相等性或顺序方面的比较方式。

  • 属性管理: 借助 __getattr__、__setattr__ 和 __delattr__ 等方法,您可以控制属性访问并实现动态行为。

  • 初始化与构造: __new__、__init__ 和 __del__ 分别负责对象的创建、初始化与清理。

  • 字符串表示: 如 __str__ 和 __repr__ 决定对象转为字符串时的表示,有助于输出更友好。

  • 迭代与容器行为: 实现 __iter__ 和 __next__ 让对象可迭代,并通过其他方法支持索引与长度获取。

  • 可调用性: __call__ 让实例可像函数一样被调用,支持带状态的函数式编程风格。

  • 上下文管理: 借助 __enter__ 和 __exit__,对象可以与语句一起正确管理资源。

  • 元编程钩子: 如 __init_subclass__ 等方法可用于动态定制类的创建与行为。

有关这些双下划线方法及其功能的完整详尽列表,请参阅官方 Python 数据模型文档。该综合资源将帮助您释放 Python 对象模型的全部威力,写出更优雅高效的代码。

真实案例与使用场景

通过几个示例,看看双下划线方法如何在真实场景中发挥作用。

运算符重载

设想您有一个自定义的数值或字符串类。通过实现 __add__ 或 __mul__ 等双下划线方法,您的对象就能自然地与算术运算符配合。您可以像使用内置类型一样对实例进行加、减或乘运算,让自定义类与 Python 的算术操作无缝融合。

class Vector:
   def __init__(self, x, y):
        self.x = x
        self.y = y
   def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
   def __str__(self):
        return f"Vector({self.x}, {self.y})"

# Usage
v1 = Vector(1, 2)
v2 = Vector(3, 4)

result = v1 + v2
print(result)  # Output: Vector(4, 6)

借助可调用性进行缓存

是否想避免重复进行代价高昂的计算?通过实现 __call__ 方法,您可以让对象表现得像一个会缓存结果的函数。这样可以存储一次重型计算的结果,并在后续调用时快速返回,从而优化性能,节省宝贵的计算时间。

class CachedComputation:
   def __init__(self):
        self.cache = {}
   def __call__(self, x):
        if x not in self.cache:
            self.cache[x] = self._expensive_computation(x)
        return self.cache[x]
   def _expensive_computation(self, x):
        # Imagine a complex calculation here
        return x ** 2

# Usage
compute = CachedComputation()

print(compute(5))  # Computes and caches: Output: 25
print(compute(5))  # Retrieves from cache: Output: 25

自定义上下文管理器

使用 __enter__ 和 __exit__ 方法构建上下文管理器,可以自动化资源管理。例如,您可以设计一个自定义上下文管理器来处理文件操作——进入代码块时打开文件,退出时确保关闭。这样既能简化代码,又能避免资源泄漏等常见错误。

class FileHandler:
   def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
   def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file
   def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()

# Usage
with FileHandler("example.txt", "r") as file:
    content = file.read()
    print(content)

自定义可迭代对象与容器

若要让自定义容器适配 Python 的循环与推导式,请通过定义 __iter__ 和 __next__ 等方法来实现迭代协议。无论您是在构建自定义栈、队列,还是其他容器,这些方法都能让对象像普通列表一样被遍历,提升类的灵活性与可用性。

class Stack:
   def __init__(self):
        self.items = []
   def push(self, item):
	self.items.append(item)
   def pop(self):
        return self.items.pop()
   def __iter__(self):
        self.index = 0
        return self
   def __next__(self):
        if self.index >= len(self.items):
            raise StopIteration
        item = self.items[self.index]
        self.index += 1
        return item

# Usage
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)

for item in stack:
    print(item)  # Output: 1, 2, 3

关于自定义可迭代对象与容器行为的更多内容,请查看Python Lists Tutorial 和 Python List Functions & Methods Tutorial and Examples。

双下划线方法的最佳实践

了解何时以及如何重写双下划线方法至关重要。请记住以下准则:

何时重写

仅在需要自定义行为时重写双下划线方法。例如,如果您希望对象以特定方式支持算术或相等性检查,那么实现 __add__ 或 __eq__ 等方法就很有意义。

一致性

如果您重写了某个方法,例如用于相等性检查的 __eq__,请相应更新相关方法,如 __hash__。这种一致性可确保对象在集合(如 set 或字典)中表现正确。

避免滥用

不要试图在标准 Python 数据模型之外创造新的双下划线名称。坚持使用内置的双下划线方法集合能让代码更清晰,也能避免意外行为。

性能考量

请记住,在代码的高频路径中过度重写双下划线方法会影响性能。应追求高效实现,避免在高频操作中造成性能下降。

结语与延伸资源

双下划线方法让您的 Python 类能够与语言的内置特性无缝交互,从而让代码更简洁、更直观。它们让对象从算术与比较到自定义属性管理的方方面面都能自如处理,而无需您额外操心。建议您在项目中尝试这些方法,体会它们如何简化复杂操作。

如需更深入的信息,请查看官方 Python 数据模型文档。您也会发现我们的 DataCamp 资源很有帮助:


Oluseye Jeremiah's photo
Author
Oluseye Jeremiah
LinkedIn

专注于人工智能、机器学习与数据科学的技术写作者,擅长将复杂概念讲解得清晰易懂。

主题
Python

通过 DataCamp 学习 Python

课程

Python 中级

4 小时
1.4M
通过使用 Matplotlib 创建可视化并用 pandas 操作 DataFrame,提升你的数据科学技能。
查看详情开始课程

创建您的免费账户

实践并学习路径你的进步。

使用 Google 继续
继续操作即表示您接受我们的《使用条款》和《隐私政策》,并同意您的数据存储在美国。
查看更多