Python 魔法方法 如何使用魔法方法编写更好的 Python 类 什么是 dunder 方法? 通过示例学习 一个基本的 TimePeriod 类 一个更好的 TimePeriod 类 添加更多功能 其他有用的魔法方法 总结 参考文献和延伸阅读

2025-06-04

Python 魔法方法如何使用魔法方法编写更好的 Python 类

什么是 dunder 方法?

通过示例学习

基本 TimePeriod 类

更好的 TimePeriod 类

添加更多功能

其他有用的魔法方法

综上所述

参考文献和进一步阅读

确保代码清晰易读至关重要,这样即使不熟悉代码库的人也能轻松理解其功能。处理面向对象的 Python 代码时,使用双下划线方法(也称为魔法方法)是实现此目标的有效方法之一。它们允许我们的用户定义类使用 Python 的内置和原始结构(例如+*/等运算符以及诸如newor 之类的关键字len),这些通常比类方法链更加直观。

什么是 dunder 方法?

如果你使用 Python 编程已有一段时间,那么你很可能遇到过一些名称以双下划线开头和结尾的奇怪方法。如果你使用过 Python 类,你甚至可能熟悉一个特定的方法:__init__,它充当构造函数,在类初始化时被调用。

__init__是 dunder(双下划线)方法的一个示例,也称为魔术方法。它们提供了一种方法,让我们可以“告诉”Python 如何处理自定义类上的原始操作,例如加法或相等性检查。例如,当您尝试使用“+”运算符添加两个自定义类的实例时,Python 解释器会进入类的定义内部,查找__add__魔术方法的实现。如果已实现,它将执行其中的代码,就像任何其他“常规”方法或函数一样。

通过示例学习

让我们实现一个“时间段”类,并看看如何使用 Python 的魔法方法使其更清晰易读。为了简单起见,我们的类只处理小时和分钟,并提供一些基本功能,例如添加和比较时间段。

基本 TimePeriod 类

这个类的基本实现可能看起来像这样:

class TimePeriod:

    def __init__(self, hours=0, minutes=0):
        self.hours = hours
        self.minutes = minutes

    def add(self, other):
        minutes = self.minutes + other.minutes
        hours = self.hours + other.hours

        if minutes >= 60:
            minutes -= 60
            hours += 1

        return TimePeriod(hours, minutes)

    def greater_than(self, other):
        if self.hours > other.hours:
            return True
        elif self.hours < other.hours:
            return False
        elif self.minutes > other.minutes:
            return True
        else:
            return False
Enter fullscreen mode Exit fullscreen mode

然后我们可以像这样创建类的实例:

time_i_sleep = TimePeriod(9, 0)
time_i_work = TimePeriod(0, 30)
Enter fullscreen mode Exit fullscreen mode

并对它们执行如下操作:

print(time_i_sleep.greater_than(time_i_work)) # Should print True
Enter fullscreen mode Exit fullscreen mode

这段代码功能上没有任何问题,完全符合预期,没有任何 bug。但是,如果我们想执行更复杂的操作,比如比较两个时间段的总和与另外两个时间段的总和,该怎么办呢?

time_i_sleep.add(time_i_watch_netflix).greater_than(time_i_work.add(time_i_do_chores))
Enter fullscreen mode Exit fullscreen mode

嗯,看起来不太好,不是吗?读者必须深入阅读每个字才能理解代码的作用。

聪明的开发者可能会把这两个和拆成两个临时变量,然后再进行比较。这确实能稍微提升代码的清晰度:

time_spent_unproductively = time_i_sleep.add(time_i_watch_netflix)
time_spent_productively = time_i_work.add(time_i_do_chores)
time_spent_unproductively.greater_than(time_spent_productively)
Enter fullscreen mode Exit fullscreen mode

更好的 TimePeriod 类

但最好的解决方案是实现 Python 的魔法方法,并将我们的逻辑移到其中。在这里,我们将实现与“+”和“>”运算符对应的__add__and__gt__方法。现在就来试试吧:

class TimePeriod:

    def __init__(self, hours=0, minutes=0):
        self.hours = hours
        self.minutes = minutes

    def __add__(self, other):
        minutes = self.minutes + other.minutes
        hours = self.hours + other.hours

        if minutes >= 60:
            minutes -= 60
            hours += 1

        return TimePeriod(hours, minutes)

    def __gt__(self, other):
        if self.hours > other.hours:
            return True
        elif self.hours < other.hours:
            return False
        elif self.minutes > other.minutes:
            return True
        else:
            return False
Enter fullscreen mode Exit fullscreen mode

现在,我们可以将复杂的操作重写为:

(time_i_sleep + time_i_watch_netflix) > (time_i_work + time_i_do_chores)
Enter fullscreen mode Exit fullscreen mode

好了!代码更加清晰易读,因为我们现在可以使用像“+”和“>”这样易于识别的符号了。现在,任何阅读我们代码的人都能一眼看出它到底做了什么。

添加更多功能

如果我们想比较两个 TimePeriod 对象,并使用 '==' 运算符检查它们是否相等,该怎么办?我们可以简单地实现eq方法,如下所示:

def __eq__(self, other):
    return self.hours == other.hours and self.minutes == other.minutes
Enter fullscreen mode Exit fullscreen mode

Python 的魔法方法并不仅限于算术和比较运算。其中一个最有用的方法,也是你可能经常遇到的,就是__str__,它允许你创建类的易于阅读的字符串表示。

def __str__(self):
    return f"{self.hours} hours, {self.minutes} minutes"
Enter fullscreen mode Exit fullscreen mode

您可以通过调用来获取此字符串表示形式str(time_period),这在调试和日志记录中很有用。

如果我们愿意,我们甚至可以将我们的课程变成某种字典!

def __getitem__(self, item):
    if item == 'hours':
        return self.hours
    elif item == 'minutes':
        return self.minutes
    else:
        raise KeyError()
Enter fullscreen mode Exit fullscreen mode

这将允许我们使用字典访问语法 [] 来访问时间段内的小时数(使用 ['hours']),以及分钟数(使用 ['minutes'])。在所有其他情况下,它会引发 KeyError,指示键不存在。

其他有用的魔法方法

Python 的 dunder 方法完整列表可在Python 语言参考中找到。这里,我列出了一些最有趣的方法。欢迎在评论区提出补充建议。

  1. __new__:虽然该__init__方法(我们已经很熟悉了)在初始化类实例时被调用,但该__new__方法的调用时间更早,在实际创建实例时就被调用了。您可以在这里阅读更多相关信息。
  2. __call__:该__call__方法允许我们的类实例可调用,就像方法或函数一样!此类可调用类在 Django 中被广泛使用,例如在中间件中。
  3. __len__:这允许您定义 Python 内置len()函数的实现。
  4. __repr__:这与魔术方法类似__str__,因为它允许您定义类的字符串表示形式。然而,区别在于,__str__前者面向最终用户,提供更用户友好、更非正式的字符串;__repr__后者面向开发人员,可能包含有关类内部状态的更复杂信息。您可以在此处阅读更多关于此区别的信息。
  5. __setitem__:我们已经看过这个__getitem__方法了。__setitem__是同一枚硬币的另一面——前者允许获取与键对应的值,而后者允许设置值。
  6. __enter__& __exit__感谢 Waylon Walker 对此的评论。这两个方法一起使用时,可将您的类用作上下文管理器,这是一种确保重要代码(尤其是在处理文件等外部资源时)始终执行的有效方法。

综上所述

我们已经看到,只需对代码进行一些细微的调整,就能显著提升代码的清晰度和可读性,并让我们能够使用强大的 Python 语言特性。当然,本文只是粗略地介绍了魔法方法所能提供的功能,因此我建议您浏览以下链接,探索它们有趣的新用例和可能性。

如果您发现本文中有任何错误或想提出任何类型的改进建议,请随时在下面发表评论。

该图片由Gerald FriedrichPixabay上发布

参考文献和进一步阅读

Python 魔法方法讲解
Python 魔法方法指南
Python 魔法方法示例

文章来源:https://dev.to/bikramjeetsingh/how-to-write-better-python-classes-using-magic-methods-4166
PREV
如何在 Express JS 中创建后端 API
NEXT
如何设置新的 MacBook Pro 开发环境