Python魔法方法全解:从入门到运算符重载的完整指南

 更新时间:2026年11月10日 09:27:03   作者:小庄-Python办公  
本文全面解析Python魔法方法(双下划线方法),涵盖构造销毁、字符串表示、比较与算术运算符重载、容器化对象、可调用对象及上下文管理器等问题,文内并附有常用魔法方法速查表和常见坑提示,助你快速掌握这一核心特性

本篇目标

  • 理解什么是魔法方法(双下划线方法)
  • 掌握常用魔法方法的写法
  • 学会运算符重载,让对象支持 +、==、< 等
  • 实现容器化对象与上下文管理器

一、什么是魔法方法

形如 __xxx__ 的方法,Python 在特定场景自动调用它们。

class Book:
    def __init__(self, title, price):
        self.title = title
        self.price = price

    def __str__(self):
        return f"《{self.title}》 ¥{self.price}"

    def __len__(self):
        return len(self.title)

b = Book("Python入门", 59)
print(b)          # 《Python入门》 ¥59     自动调 __str__
print(len(b))     # 11                      自动调 __len__

二、构造与销毁

方法调用时机
__init__(self, ...)创建对象后初始化
__new__(cls, ...)创建对象时(很少用)
__del__(self)对象被垃圾回收时
class Resource:
    def __init__(self, name):
        self.name = name
        print(f"{name} 被创建")

    def __del__(self):
        print(f"{self.name} 被销毁")

r = Resource("连接")
del r       # 连接 被销毁

三、字符串表示

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __str__(self):
        """给终端用户看:print()、str()"""
        return f"({self.x}, {self.y})"

    def __repr__(self):
        """给开发者看:调试、交互式环境、容器里打印"""
        return f"Point({self.x}, {self.y})"

    def __format__(self, spec):
        """支持 f-string 的自定义格式"""
        if spec == "verbose":
            return f"横坐标={self.x}, 纵坐标={self.y}"
        return str(self)

p = Point(3, 4)
print(f"{p}")              # (3, 4)
print(f"{p:verbose}")      # 横坐标=3, 纵坐标=4
print([p])                 # [Point(3, 4)]   容器里用 __repr__

经验:至少写 __repr__,调试时能省很多事。

四、比较运算符重载

class Money:
    def __init__(self, amount):
        self.amount = amount

    def __eq__(self, other):
        return self.amount == other.amount

    def __lt__(self, other):
        return self.amount < other.amount

    def __le__(self, other):
        return self.amount <= other.amount

    def __gt__(self, other):
        return self.amount > other.amount

    def __ge__(self, other):
        return self.amount >= other.amount

    def __ne__(self, other):
        return self.amount != other.amount

m1, m2 = Money(100), Money(200)
print(m1 < m2)         # True
print(sorted([m2, m1]))    # 能排序了

用 functools.total_ordering 偷懒

只写 __eq__ 和 __lt__,其余自动生成:

from functools import total_ordering

@total_ordering
class Money:
    def __init__(self, amount):
        self.amount = amount

    def __eq__(self, other):
        return self.amount == other.amount

    def __lt__(self, other):
        return self.amount < other.amount

    def __repr__(self):
        return f"Money({self.amount})"

别忘了hash

定义了 __eq__ 后,对象默认不可哈希,不能放进集合或做字典 key:

class Money:
    def __init__(self, amount):
        self.amount = amount
    def __eq__(self, other):
        return self.amount == other.amount
    def __hash__(self):
        return hash(self.amount)      # 补上就能哈希了

{Money(1), Money(2)}     # ✅

五、算术运算符重载

class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, n):
        return Vector(self.x * n, self.y * n)

    def __rmul__(self, n):
        return self.__mul__(n)        # 支持 n * vector

    def __neg__(self):
        return Vector(-self.x, -self.y)

    def __abs__(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)      # Vector(4, 6)
print(v2 - v1)      # Vector(2, 2)
print(v1 * 3)       # Vector(3, 6)
print(3 * v1)       # Vector(3, 6)   ← 靠 __rmul__
print(abs(v2))      # 5.0

完整算术方法对照

表达式方法
a + b__add__
a - b__sub__
a * b__mul__
a / b__truediv__
a // b__floordiv__
a % b__mod__
a ** b__pow__
-a__neg__
abs(a)__abs__
a += b__iadd__

六、容器化魔法方法

让自己的类像列表一样工作:

class Playlist:
    def __init__(self):
        self._songs = []

    def add(self, song):
        self._songs.append(song)

    def __len__(self):
        return len(self._songs)

    def __getitem__(self, index):
        return self._songs[index]        # 支持下标和切片

    def __setitem__(self, index, value):
        self._songs[index] = value

    def __contains__(self, item):
        return item in self._songs       # 支持 in

    def __iter__(self):
        return iter(self._songs)         # 支持 for 遍历

    def __repr__(self):
        return f"Playlist({len(self)} songs)"

pl = Playlist()
pl.add("晴天")
pl.add("稻香")

print(len(pl))          # 2
print(pl[0])            # 晴天
print("稻香" in pl)      # True
for song in pl:         # 能遍历
    print(song)

实现 __getitem__ + __len__ + __iter__,类就"像"个列表了。

七、可调用对象call

class Counter:
    def __init__(self):
        self.count = 0

    def __call__(self):
        self.count += 1
        return self.count

c = Counter()
print(c())      # 1      像函数一样调用
print(c())      # 2
print(callable(c))    # True

常用于带状态的"函数"(装饰器就会用到)。

八、上下文管理器enter/exit

让自己的类支持 with 语句:

class Timer:
    import time

    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.elapsed = time.time() - self.start
        print(f"耗时 {self.elapsed:.3f} 秒")
        return False      # False = 不吞异常

with Timer() as t:
    sum(range(10_000_00))
# 耗时 0.045 秒

实战:数据库连接管理

class DBConnection:
    def __init__(self, dsn):
        self.dsn = dsn
        self.conn = None

    def __enter__(self):
        print(f"连接数据库 {self.dsn}")
        self.conn = {"connected": True}
        return self.conn

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("关闭数据库连接")
        self.conn = None
        if exc_type:
            print(f"发生异常:{exc_val}")
        return False

with DBConnection("sqlite:///app.db") as conn:
    print(conn)

九、常用魔法方法速查

分类方法
构造销毁__init__ __new__ __del__
字符串__str__ __repr__ __format__
比较__eq__ __lt__ __le__ __gt__ __ge__ __ne__ __hash__
算术__add__ __sub__ __mul__ __truediv__ __neg__ __abs__
容器__len__ __getitem__ __setitem__ __contains__ __iter__
调用__call__
上下文__enter__ __exit__
属性__getattr__ __setattr__ __getattribute__
布尔__bool__

常见坑

坑说明
定义 __eq__ 后对象不能哈希补 __hash__
__repr__ 里调 str(self) 造成递归直接写字段
__exit__ 返回 True 吞异常一般不吞,返回 False 或 None
__getattr__ 写错导致无限递归记得 raise AttributeError
算术方法返回了 None必须 return 新对象

本篇小结

  • 魔法方法是 Python 自动调用的双下划线方法
  • 必写:__init__、__repr__;常用:__str__、__eq__、__len__
  • 运算符重载让对象支持 +、<、in、for
  • __enter__ / __exit__ 实现 with 支持

练习题

  1. 给 Money 类加 __add__、__sub__,支持两笔钱相加减。
  2. 写一个 Stack 类,支持 push、pop、len()、下标访问、in 判断、for 遍历。
  3. 用 __enter__/__exit__ 写一个"临时切换工作目录"的上下文管理器。

以上就是Python魔法方法全解:从入门到运算符重载的完整指南的详细内容,更多关于Python魔法方法的资料请关注脚本之家其它相关文章!

相关文章

  • Python Scrapy图片爬取原理及代码实例

    Python Scrapy图片爬取原理及代码实例

    这篇文章主要介绍了Python Scrapy图片爬取原理及代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • python使用opencv切割图片白边

    python使用opencv切割图片白边

    这篇文章主要为大家详细介绍了python使用opencv切割图片的白边,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一
    2021-09-09
  • Pandas常用累计、同比、环比等统计方法实践过程

    Pandas常用累计、同比、环比等统计方法实践过程

    这篇文章主要介绍了Pandas常用累计、同比、环比等统计方法实践过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • 解决Pytorch 加载训练好的模型 遇到的error问题

    解决Pytorch 加载训练好的模型 遇到的error问题

    今天小编就为大家分享一篇解决Pytorch 加载训练好的模型 遇到的error问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • Python map和reduce函数用法示例

    Python map和reduce函数用法示例

    这篇文章主要介绍了Python map和reduce函数用法示例,本文给出了两个函数的多个用法示例,需要的朋友可以参考下
    2015-02-02
  • 利用Python发送邮件或发带附件的邮件

    利用Python发送邮件或发带附件的邮件

    这篇文章主要介绍了如何利用Python发送邮件或发带附件的邮件,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下。
    2020-11-11
  • python常用文件操作(读写追加等)

    python常用文件操作(读写追加等)

    在Python中,文件操作是一项常用的任务,本节将介绍如何执行基本的文件操作,如读取、写入和追加数据,我们将通过实例代码详细讲解每个知识点
    2023-06-06
  • Python中isinstance和hasattr的实现示例

    Python中isinstance和hasattr的实现示例

    本文详细介绍了Python中的两个内置函数isinstance和hasattr,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • Python实战教学之如何将HTML内容保存为图像文件

    Python实战教学之如何将HTML内容保存为图像文件

    在开发工作中,我们有时会遇到需要将 HTML 内容固化为图像文件的场景,本文将介绍如何借助 Spire.Doc for Python 完成 HTML 转图像这一任务,涵盖从 HTML 文件与 HTML 字符串两种输入形式到 PNG 图片的转换流程,希望对大家有所帮助
    2026-10-10
  • 解决Tensorflow占用GPU显存问题

    解决Tensorflow占用GPU显存问题

    今天小编就为大家分享一篇解决Tensorflow占用GPU显存问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-02-02

最新评论