飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

魔术方法

魔术方法(Magic Methods),也称为双下划线方法(Dunder Methods),是 Python 对象模型的核心。它们让自定义类能够响应内置函数和运算符,使得用户定义的对象可以像内置类型一样自然工作。

字符串表示:str 与 repr

__str__ 和 __repr__ 控制对象的字符串表示形式,但服务于不同场景。

__repr__ 的目标是无歧义:返回的字符串应当尽可能准确地表示对象,理想情况下可以作为代码重新创建该对象。__str__ 的目标是可读性:返回面向用户的友好描述。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __repr__(self):
        return f'Point({self.x!r}, {self.y!r})'
    
    def __str__(self):
        return f'({self.x}, {self.y})'

p = Point(3, 4)
print(str(p))       # (3, 4)  —— 调用 __str__
print(repr(p))      # Point(3, 4) —— 调用 __repr__
print(p)            # (3, 4)  —— print 调用 __str__

如果类未定义 __str__,str() 和 print() 会回退到 __repr__。反之则不会回退。因此,至少实现 __repr__ 是良好的实践。如果 __repr__ 本身已经足够友好,也可以只定义 __repr__ 而不定义 __str__。

比较操作

__eq__ 定义相等比较(==),__lt__ 定义小于(<),__le__ 定义小于等于(<=),__gt__ 和 __ge__ 定义大于相关操作。__ne__ 默认由 __eq__ 推导,通常不需要显式定义。

class Score:
    def __init__(self, value):
        self.value = value
    
    def __eq__(self, other):
        if not isinstance(other, Score):
            return NotImplemented
        return self.value == other.value
    
    def __lt__(self, other):
        if not isinstance(other, Score):
            return NotImplemented
        return self.value < other.value

s1 = Score(85)
s2 = Score(90)
print(s1 == s2)     # False
print(s1 < s2)      # True
print(s1 <= s2)     # True —— Python 自动由 __lt__ 和 __eq__ 推导

当比较操作返回 NotImplemented 时,Python 会尝试调用另一个操作数的对应反射方法(如 __gt__),或最终回退到默认行为。这比直接抛出 TypeError 更友好,允许不同类型的对象参与比较。

如果定义了 __eq__ 但未定义 __hash__,类的实例将变为不可哈希,无法放入 set 或作为 dict 的键。若对象是可变的,通常应将 __hash__ 设为 None 来明确禁止哈希:

class MutablePoint:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __eq__(self, other):
        if isinstance(other, MutablePoint):
            return self.x == other.x and self.y == other.y
        return NotImplemented
    
    __hash__ = None   # 显式声明不可哈希

容器协议

实现容器协议让自定义类支持 len()、[] 索引、in 成员测试和 for 迭代。

class Stack:
    def __init__(self):
        self._items = []
    
    def push(self, item):
        self._items.append(item)
    
    def __len__(self):
        return len(self._items)
    
    def __getitem__(self, index):
        """支持 s[0], s[-1], s[1:3] 等"""
        return self._items[index]
    
    def __contains__(self, item):
        return item in self._items
    
    def __iter__(self):
        return iter(self._items)

s = Stack()
s.push('a')
s.push('b')
s.push('c')

print(len(s))        # 3
print(s[0])          # a
print(s[-1])         # c
print('b' in s)      # True
print(list(s))       # ['a', 'b', 'c']

__getitem__ 接收的 index 不仅可以是整数,还可以是 slice 对象。如果未实现 __iter__ 但实现了 __getitem__,Python 会尝试通过从 0 开始递增索引来迭代对象(旧式迭代协议)。

可调用对象:call

实现 __call__ 的实例可以像函数一样被调用。这在需要维护状态的场景中非常有用,例如策略对象、回调函数和装饰器。

class Multiplier:
    def __init__(self, factor):
        self.factor = factor
    
    def __call__(self, value):
        return value * self.factor

double = Multiplier(2)
triple = Multiplier(3)

print(double(5))    # 10
print(triple(5))    # 15
print(callable(double))   # True

callable(obj) 检查对象是否可调用。如果类定义了 __call__,callable() 返回 True。

算术运算符

算术魔术方法让对象支持 +、-、* 等运算符。以 __add__ 为例,当执行 a + b 时,Python 首先调用 a.__add__(b);如果返回 NotImplemented,再尝试 b.__radd__(a)。

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x + other.x, self.y + other.y)
        return NotImplemented
    
    def __radd__(self, other):
        return self.__add__(other)
    
    def __repr__(self):
        return f'Vector({self.x!r}, {self.y!r})'

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)        # Vector(4, 6)

对于数值类型,还应考虑实现 __iadd__(+=)等原地操作。如果未定义原地方法,a += b 会回退到 a = a + b。

迭代器协议

迭代器协议要求对象实现 __iter__ 返回自身,以及 __next__ 返回下一个元素或在结束时抛出 StopIteration。

class Countdown:
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.start <= 0:
            raise StopIteration
        self.start -= 1
        return self.start + 1

for n in Countdown(5):
    print(n, end=' ')   # 5 4 3 2 1

更常见的做法是让 __iter__ 返回一个生成器,利用生成器自动保存状态的能力:

class BetterCountdown:
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1

常用魔术方法速查

类别方法触发方式
生命周期__init__, __del__构造、析构
字符串__str__, __repr__, __format__str(), repr(), format()
比较__eq__, __lt__, __le__, __gt__, __ge__, __ne__==, <, <=, >, >=, !=
算术__add__, __sub__, __mul__, __truediv__, __pow__+, -, *, /, **
反射算术__radd__, __rsub__, ...当左操作数不支持时
容器__len__, __getitem__, __setitem__, __delitem__, __contains__len(), [], del [], in
迭代__iter__, __next__for, iter(), next()
可调用__call__obj()
哈希__hash__hash(), set, dict 键
布尔__bool__bool(), if 条件
属性访问__getattr__, __getattribute__, __setattr__, __delattr__属性读写删除

魔术方法的设计哲学是:不需要记忆全部名称,而是理解"想让对象支持什么操作,就实现对应的 __xxx__"。查阅官方文档的 Data Model 章节可获得完整列表。

上一页
多重继承
下一页
属性装饰器