目 录CONTENT

文章目录

Python的魔法方法和双划线方法,在类里发挥威力

Administrator
2025-10-09 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

 

字数 4090,阅读大约需 21 分钟

在使用Python进行面向对象编程:

Python会自动调用魔术方法以响应某些操作,例如实例化、序列索引、属性管理等等。魔术方法支持Python中的核心面向对象特性,因此对于Python程序员来说,了解它们是至关重要的。
魔法方法是高频使用语法,掌握使用,可以帮助我们更好的学习和实践Python代码!

什么是魔法方法

在Python中,特殊方法也被称为魔术方法或双下方法。后一种术语dunder指的是Python用于命名其特殊方法和属性的特定命名约定。该约定是在名称的前后使用双下划线,因此看起来像.method()。
Magic or Dunder Methods in Python[1]
比如__init__(),类初始化器,完整的列表可以在3. Data model — Python 3.14.0 documentation[2]学习到。主要可以分类为:

  1. 1. Basic customization 基本定制

  2. 2. Customizing attribute access 自定义属性访问

  3. 3. Customizing class creation 自定义类的创建

  4. 4. Customizing instance and subclass checks

  5. 5. 自定义实例和子类检查

  6. 6. Emulating generic types 模拟泛型类型

  7. 7. Emulating callable objects 模拟可调用对象

  8. 8. Emulating container types 模拟容器类型

  9. 9. Emulating numeric types 模拟数值类型

  10. 10. with statement context managers with语句上下文管理器

  11. 11. Customizing positional arguments in class pattern matching

  12. 12. 在类模式匹配中自定义位置参数

  13. 13. Emulating buffer types 模拟缓冲区类型

  14. 14. Special method lookup 特殊方法查找

A method that is called implicitly by Python to execute a certain operation on a type, such as addition. Such methods have names starting and ending with double underscores.意味在特定类型操作,Python会自动帮助我们执行调用魔法方法


    
    
    
  print(5+2)
print((5).__add__(2))

更多阅读链接:Python's Magic Methods: Leverage Their Power in Your Classes – Real Python[3]

注意警告

由于魔术方法对Python本身具有特殊意义,因此您应该避免使用前后双下划线来命名自定义方法。

演示代码


    
    
    
  """
Python 魔法方法(特殊方法)教学代码
Magic Methods (Special Methods) Tutorial

魔法方法是Python中以双下划线开头和结尾的特殊方法,用于实现对象的特定行为。
它们让你的类可以像内置类型一样工作,提供更加pythonic的接口。
"""

print("=" * 80)
print("第一部分:魔法方法基础概念")
print("=" * 80)

# 示例1:最基础的魔法方法演示
print("\n【示例1】基础运算符的魔法方法")
print("普通写法: 5 + 2 =", 5 + 2)
print("魔法方法写法: (5).__add__(2) =", (5).__add__(2))
print("说明:当我们写 5+2 时,Python实际上调用的是 (5).__add__(2)")


print("\n" + "=" * 80)
print("第二部分:对象初始化和表示")
print("=" * 80)

# 示例2:__init__, __str__, __repr__
class Person:
    """
    人员类 - 演示基础魔法方法
    """
    
    def __init__(self, name, age):
        """
        初始化方法 - 创建对象时自动调用
        参数:
            name: 姓名
            age: 年龄
        """
        self.name = name
        self.age = age
        print(f"__init__被调用:创建了 {name} 对象")
    
    def __str__(self):
        """
        字符串表示 - 用于print()和str()
        返回用户友好的字符串
        """
        return f"Person({self.name}, {self.age}岁)"
    
    def __repr__(self):
        """
        开发者表示 - 用于调试,应该返回能重建对象的表达式
        """
        return f"Person('{self.name}', {self.age})"
    
    def __del__(self):
        """
        析构方法 - 对象被销毁时调用
        """
        print(f"__del__被调用:{self.name} 对象被销毁")


print("\n【示例2】对象的创建和表示")
p1 = Person("张三", 25)
print(f"使用str(): {str(p1)}")  # 调用__str__
print(f"使用repr(): {repr(p1)}")  # 调用__repr__
print(f"直接print: {p1}")  # 默认调用__str__


print("\n" + "=" * 80)
print("第三部分:算术运算符重载")
print("=" * 80)

# 示例3:实现自定义的向量类
class Vector:
    """
    二维向量类 - 演示算术运算符重载
    """
    
    def __init__(self, x, y):
        """初始化向量"""
        self.x = x
        self.y = y
    
    def __str__(self):
        """字符串表示"""
        return f"Vector({self.x}, {self.y})"
    
    def __add__(self, other):
        """
        加法运算符 + 的重载
        实现:v1 + v2
        """
        return Vector(self.x + other.x, self.y + other.y)
    
    def __sub__(self, other):
        """
        减法运算符 - 的重载
        实现:v1 - v2
        """
        return Vector(self.x - other.x, self.y - other.y)
    
    def __mul__(self, scalar):
        """
        乘法运算符 * 的重载(标量乘法)
        实现:v * 3
        """
        return Vector(self.x * scalar, self.y * scalar)
    
    def __truediv__(self, scalar):
        """
        除法运算符 / 的重载
        实现:v / 2
        """
        return Vector(self.x / scalar, self.y / scalar)
    
    def __neg__(self):
        """
        负号运算符 - 的重载(一元运算)
        实现:-v
        """
        return Vector(-self.x, -self.y)
    
    def __abs__(self):
        """
        绝对值函数 abs() 的重载
        返回向量的模(长度)
        """
        return (self.x ** 2 + self.y ** 2) ** 0.5


print("\n【示例3】向量运算")
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(f"v1 = {v1}")
print(f"v2 = {v2}")
print(f"v1 + v2 = {v1 + v2}")
print(f"v1 - v2 = {v1 - v2}")
print(f"v1 * 3 = {v1 * 3}")
print(f"v1 / 2 = {v1 / 2}")
print(f"-v1 = {-v1}")
print(f"|v1| = {abs(v1)}")


print("\n" + "=" * 80)
print("第四部分:比较运算符重载")
print("=" * 80)

# 示例4:实现可比较的类
class Student:
    """
    学生类 - 演示比较运算符重载
    """
    
    def __init__(self, name, score):
        """初始化学生信息"""
        self.name = name
        self.score = score
    
    def __str__(self):
        """字符串表示"""
        return f"{self.name}(分数: {self.score})"
    
    def __eq__(self, other):
        """
        等于运算符 == 的重载
        """
        return self.score == other.score
    
    def __ne__(self, other):
        """
        不等于运算符 != 的重载
        """
        return self.score != other.score
    
    def __lt__(self, other):
        """
        小于运算符 < 的重载
        """
        return self.score < other.score
    
    def __le__(self, other):
        """
        小于等于运算符 <= 的重载
        """
        return self.score <= other.score
    
    def __gt__(self, other):
        """
        大于运算符 > 的重载
        """
        return self.score > other.score
    
    def __ge__(self, other):
        """
        大于等于运算符 >= 的重载
        """
        return self.score >= other.score


print("\n【示例4】学生成绩比较")
s1 = Student("李四", 85)
s2 = Student("王五", 92)
s3 = Student("赵六", 85)
print(f"s1 = {s1}")
print(f"s2 = {s2}")
print(f"s3 = {s3}")
print(f"s1 == s3: {s1 == s3}")
print(f"s1 < s2: {s1 < s2}")
print(f"s2 > s1: {s2 > s1}")
print(f"排序结果: {sorted([s1, s2, s3], key=lambda s: s.score, reverse=True)}")


print("\n" + "=" * 80)
print("第五部分:容器类型魔法方法")
print("=" * 80)

# 示例5:实现自定义列表类
class MyList:
    """
    自定义列表类 - 演示容器类型魔法方法
    """
    
    def __init__(self):
        """初始化内部存储"""
        self._items = []
    
    def __len__(self):
        """
        长度函数 len() 的重载
        返回容器中元素的数量
        """
        return len(self._items)
    
    def __getitem__(self, index):
        """
        索引访问 [] 的重载(读取)
        实现:my_list[0]
        """
        return self._items[index]
    
    def __setitem__(self, index, value):
        """
        索引访问 [] 的重载(设置)
        实现:my_list[0] = value
        """
        self._items[index] = value
    
    def __delitem__(self, index):
        """
        删除元素 del 的重载
        实现:del my_list[0]
        """
        del self._items[index]
    
    def __contains__(self, item):
        """
        成员测试 in 的重载
        实现:item in my_list
        """
        return item in self._items
    
    def __iter__(self):
        """
        迭代器协议 - 使对象可迭代
        实现:for item in my_list
        """
        return iter(self._items)
    
    def append(self, item):
        """添加元素"""
        self._items.append(item)
    
    def __str__(self):
        """字符串表示"""
        return f"MyList({self._items})"


print("\n【示例5】自定义列表操作")
my_list = MyList()
my_list.append(10)
my_list.append(20)
my_list.append(30)
print(f"列表内容: {my_list}")
print(f"长度 len(my_list): {len(my_list)}")
print(f"索引访问 my_list[1]: {my_list[1]}")
print(f"成员测试 20 in my_list: {20 in my_list}")
print(f"迭代访问: ", end="")
for item in my_list:
    print(item, end=" ")
print()


print("\n" + "=" * 80)
print("第六部分:上下文管理器")
print("=" * 80)

# 示例6:实现上下文管理器
class FileManager:
    """
    文件管理器 - 演示上下文管理器协议
    实现自动资源管理(类似with语句)
    """
    
    def __init__(self, filename, mode):
        """初始化文件管理器"""
        self.filename = filename
        self.mode = mode
        self.file = None
    
    def __enter__(self):
        """
        进入上下文时调用
        在with语句开始时执行
        返回值会赋给as后的变量
        """
        print(f"__enter__被调用:打开文件 {self.filename}")
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        退出上下文时调用
        在with语句结束时执行(无论是否发生异常)
        参数:
            exc_type: 异常类型
            exc_val: 异常值
            exc_tb: 异常追踪信息
        """
        print(f"__exit__被调用:关闭文件 {self.filename}")
        if self.file:
            self.file.close()
        # 返回False表示不抑制异常,返回True表示抑制异常
        return False


print("\n【示例6】上下文管理器使用")
print("使用自定义文件管理器:")
try:
    with FileManager("test/basic_test/test.txt", "r") as f:
        content = f.read()
        print(f"文件内容: {content[:50]}...")  # 只显示前50个字符
except Exception as e:
    print(f"发生错误: {e}")


print("\n" + "=" * 80)
print("第七部分:可调用对象")
print("=" * 80)

# 示例7:实现可调用对象
class Counter:
    """
    计数器类 - 演示__call__方法
    使对象可以像函数一样被调用
    """
    
    def __init__(self):
        """初始化计数器"""
        self.count = 0
    
    def __call__(self, step=1):
        """
        使对象可调用
        实现:counter() 或 counter(5)
        """
        self.count += step
        print(f"__call__被调用:当前计数 = {self.count}")
        return self.count


print("\n【示例7】可调用对象")
counter = Counter()
print("调用 counter():")
counter()  # 像函数一样调用
counter()
print("调用 counter(5):")
counter(5)
print(f"最终计数: {counter.count}")


print("\n" + "=" * 80)
print("第八部分:属性访问控制")
print("=" * 80)

# 示例8:实现属性访问控制
class DynamicAttributes:
    """
    动态属性类 - 演示属性访问魔法方法
    """
    
    def __init__(self):
        """初始化内部字典存储"""
        self._data = {}
    
    def __getattr__(self, name):
        """
        获取不存在的属性时调用
        注意:只有在属性不存在时才会调用
        """
        print(f"__getattr__被调用:尝试访问 '{name}'")
        if name in self._data:
            return self._data[name]
        return f"属性 '{name}' 不存在"
    
    def __setattr__(self, name, value):
        """
        设置属性时调用
        注意:所有属性设置都会调用此方法
        """
        print(f"__setattr__被调用:设置 {name} = {value}")
        if name == '_data':
            # 必须使用object.__setattr__避免无限递归
            object.__setattr__(self, name, value)
        else:
            self._data[name] = value
    
    def __delattr__(self, name):
        """
        删除属性时调用
        实现:del obj.attr
        """
        print(f"__delattr__被调用:删除 '{name}'")
        if name in self._data:
            del self._data[name]
    
    def __getattribute__(self, name):
        """
        访问任何属性时都会调用
        注意:此方法会拦截所有属性访问,慎用!
        """
        # 只演示,实际使用需要非常小心
        return object.__getattribute__(self, name)


print("\n【示例8】属性访问控制")
obj = DynamicAttributes()
obj.name = "Python"
obj.version = 3.9
print(f"访问 obj.name: {obj.name}")
print(f"访问不存在的属性 obj.author: {obj.author}")


print("\n" + "=" * 80)
print("第九部分:迭代器协议")
print("=" * 80)

# 示例9:实现迭代器
class Countdown:
    """
    倒计时迭代器 - 演示迭代器协议
    """
    
    def __init__(self, start):
        """初始化起始值"""
        self.current = start
    
    def __iter__(self):
        """
        返回迭代器对象本身
        使对象可以用于for循环
        """
        return self
    
    def __next__(self):
        """
        返回下一个值
        当没有更多元素时抛出StopIteration
        """
        if self.current <= 0:
            raise StopIteration
        self.current -= 1
        return self.current + 1


print("\n【示例9】自定义迭代器")
print("倒计时从5到1:")
for num in Countdown(5):
    print(num, end=" ")
print()


print("\n" + "=" * 80)
print("第十部分:其他常用魔法方法")
print("=" * 80)

# 示例10:其他魔法方法
class CompleteExample:
    """
    综合示例类 - 演示其他常用魔法方法
    """
    
    def __init__(self, value):
        """初始化"""
        self.value = value
    
    def __bool__(self):
        """
        布尔值转换
        用于:if obj, bool(obj)
        """
        return bool(self.value)
    
    def __int__(self):
        """
        整数转换
        用于:int(obj)
        """
        return int(self.value)
    
    def __float__(self):
        """
        浮点数转换
        用于:float(obj)
        """
        return float(self.value)
    
    def __hash__(self):
        """
        计算哈希值
        用于:hash(obj), 使对象可以作为字典的键或集合的元素
        """
        return hash(self.value)
    
    def __format__(self, format_spec):
        """
        格式化字符串
        用于:f"{obj:format_spec}"
        """
        if format_spec == 'hex':
            return hex(int(self.value))
        elif format_spec == 'bin':
            return bin(int(self.value))
        return str(self.value)


print("\n【示例10】其他魔法方法")
obj = CompleteExample(42)
print(f"布尔值 bool(obj): {bool(obj)}")
print(f"整数值 int(obj): {int(obj)}")
print(f"浮点值 float(obj): {float(obj)}")
print(f"哈希值 hash(obj): {hash(obj)}")
print(f"十六进制格式 {{obj:hex}}: {obj:hex}")
print(f"二进制格式 {{obj:bin}}: {obj:bin}")


print("\n" + "=" * 80)
print("第十一部分:实战综合示例 - 银行账户类")
print("=" * 80)

# 示例11:实战综合应用
class BankAccount:
    """
    银行账户类 - 综合应用多个魔法方法
    演示在实际项目中如何使用魔法方法
    """
    
    # 类变量:记录所有账户
    _accounts = []
    
    def __init__(self, owner, balance=0):
        """
        初始化账户
        参数:
            owner: 账户所有者
            balance: 初始余额
        """
        self.owner = owner
        self._balance = balance
        BankAccount._accounts.append(self)
    
    def __str__(self):
        """用户友好的字符串表示"""
        return f"账户[{self.owner}]: ¥{self._balance:.2f}"
    
    def __repr__(self):
        """开发者友好的表示"""
        return f"BankAccount('{self.owner}', {self._balance})"
    
    def __add__(self, amount):
        """
        存款操作(使用+运算符)
        account + 100 表示存入100元
        """
        if amount < 0:
            raise ValueError("存款金额必须为正数")
        return BankAccount(self.owner, self._balance + amount)
    
    def __sub__(self, amount):
        """
        取款操作(使用-运算符)
        account - 50 表示取出50元
        """
        if amount < 0:
            raise ValueError("取款金额必须为正数")
        if self._balance < amount:
            raise ValueError("余额不足")
        return BankAccount(self.owner, self._balance - amount)
    
    def __lt__(self, other):
        """比较账户余额(小于)"""
        return self._balance < other._balance
    
    def __eq__(self, other):
        """比较账户余额(等于)"""
        return self._balance == other._balance
    
    def __bool__(self):
        """
        账户是否有余额
        可用于:if account
        """
        return self._balance > 0
    
    def __float__(self):
        """返回余额的浮点值"""
        return float(self._balance)
    
    def __format__(self, format_spec):
        """
        自定义格式化输出
        支持 'simple' 和 'detail' 格式
        """
        if format_spec == 'simple':
            return f"{self.owner}: ¥{self._balance:.2f}"
        elif format_spec == 'detail':
            return f"账户所有者:{self.owner}\n余额:¥{self._balance:.2f}"
        return str(self)
    
    @property
    def balance(self):
        """余额属性(只读)"""
        return self._balance
    
    def deposit(self, amount):
        """存款方法"""
        if amount > 0:
            self._balance += amount
            return True
        return False
    
    def withdraw(self, amount):
        """取款方法"""
        if 0 < amount <= self._balance:
            self._balance -= amount
            return True
        return False


print("\n【示例11】银行账户系统实战")
# 创建账户
acc1 = BankAccount("张三", 1000)
acc2 = BankAccount("李四", 500)

print(f"初始状态:")
print(f"  {acc1}")
print(f"  {acc2}")

# 存款和取款
print(f"\n张三存入500元")
acc1.deposit(500)
print(f"  {acc1}")

print(f"\n李四取出200元")
acc2.withdraw(200)
print(f"  {acc2}")

# 比较账户
print(f"\n账户比较:")
print(f"  acc1 > acc2: {acc1 > acc2}")
print(f"  acc1 == acc2: {acc1 == acc2}")

# 布尔测试
print(f"\n布尔测试:")
print(f"  acc1有余额: {bool(acc1)}")
empty_acc = BankAccount("王五", 0)
print(f"  空账户有余额: {bool(empty_acc)}")

# 格式化输出
print(f"\n格式化输出:")
print(f"  简单格式: {acc1:simple}")
print(f"  详细格式:\n{acc1:detail}")


print("\n" + "=" * 80)
print("魔法方法总结")
print("=" * 80)
print("""
常用魔法方法分类:

1. 对象初始化和表示:
   __init__    : 初始化对象
   __str__     : 字符串表示(用户友好)
   __repr__    : 字符串表示(开发者友好)
   __del__     : 对象销毁时调用

2. 运算符重载:
   __add__     : +
   __sub__     : -
   __mul__     : *
   __truediv__ : /
   __floordiv__: //
   __mod__     : %
   __pow__     : **

3. 比较运算符:
   __eq__      : ==
   __ne__      : !=
   __lt__      : <
   __le__      : <=
   __gt__      : >
   __ge__      : >=

4. 容器类型:
   __len__     : len()
   __getitem__ : obj[key]
   __setitem__ : obj[key] = value
   __delitem__ : del obj[key]
   __contains__: item in obj
   __iter__    : for item in obj

5. 上下文管理:
   __enter__   : with语句进入
   __exit__    : with语句退出

6. 属性访问:
   __getattr__ : 访问不存在的属性
   __setattr__ : 设置属性
   __delattr__ : 删除属性

7. 可调用对象:
   __call__    : obj()

8. 其他常用:
   __bool__    : bool(obj)
   __int__     : int(obj)
   __float__   : float(obj)
   __hash__    : hash(obj)
   __format__  : format(obj)

实际应用建议:
1. 合理使用魔法方法可以让代码更加pythonic和直观
2. 不要滥用魔法方法,只在有明确需求时使用
3. 遵循Python的设计哲学:显式优于隐式
4. 魔法方法应该具有一致和可预测的行为
5. 在实现魔法方法时注意性能和副作用
""")

print("\n教学代码演示完成!")

附录:在class使用@property

property是只读属性,如果需要修改,则需要使用setter


    
    
    
  class Circle:
    """
    表示一个圆的类
    """
    def __init__(self, radius):
        """
        初始化Circle对象
        :param radius: 圆的半径
        """
        self._radius = radius

    @property
    def radius(self):
        """
        获取圆的半径
        """
        return self._radius

    @radius.setter
    def radius(self, value):
        """
        设置圆的半径
        :param value: 新的半径值
        """
        if not isinstance(value, (int, float)):
            raise TypeError("半径必须是数字")
        if value < 0:
            raise ValueError("半径不能为负数")
        self._radius = value

    @property
    def area(self):
        """
        计算并返回圆的面积
        """
        return 3.14159 * self._radius ** 2

    @property
    def circumference(self):
        """
        计算并返回圆的周长
        """
        return 2 * 3.14159 * self._radius

# -----------------------------------------------------------------------------
# 示例使用:带有setter的属性
# -----------------------------------------------------------------------------
if __name__ == "__main__":
    print("--- 演示带有setter的属性 (Circle 类) ---")
    # 创建一个Circle对象
    my_circle = Circle(5)

    # 访问属性
    print(f"初始圆的半径: {my_circle.radius}")
    print(f"初始圆的面积: {my_circle.area}")
    print(f"初始圆的周长: {my_circle.circumference}")

    # 修改半径 (通过setter)
    my_circle.radius = 10
    print(f"\n修改半径为10后:")
    print(f"圆的半径: {my_circle.radius}")
    print(f"圆的面积: {my_circle.area}")
    print(f"圆的周长: {my_circle.circumference}")

    # 尝试设置无效的半径 (通过setter的验证)
    print("\n尝试设置无效半径:")
    try:
        my_circle.radius = -2
    except ValueError as e:
        print(f"捕获到错误: {e}")

    try:
        my_circle.radius = "abc"
    except TypeError as e:
        print(f"捕获到错误: {e}")

    # -----------------------------------------------------------------------------
    # 示例使用:不带setter的属性 (只读属性)
    # -----------------------------------------------------------------------------
    print("\n--- 演示不带setter的属性 (ReadOnlyCircle 类) ---")

    class ReadOnlyCircle:
        """
        表示一个只读半径的圆的类
        """
        def __init__(self, radius):
            """
            初始化ReadOnlyCircle对象
            :param radius: 圆的半径
            """
            self._radius = radius

        @property
        def radius(self):
            """
            获取圆的半径 (只读)
            """
            return self._radius

        @property
        def area(self):
            """
            计算并返回圆的面积
            """
            return 3.14159 * self._radius ** 2

    my_readonly_circle = ReadOnlyCircle(7)
    print(f"只读圆的半径: {my_readonly_circle.radius}")
    print(f"只读圆的面积: {my_readonly_circle.area}")

    # 尝试修改只读属性
    print("\n尝试修改只读圆的半径:")
    try:
        my_readonly_circle.radius = 15
    except AttributeError as e:
        print(f"捕获到错误: {e}")
    except Exception as e:
        print(f"捕获到其他错误: {e}")

    print(f"修改尝试后,只读圆的半径仍然是: {my_readonly_circle.radius}")

引用链接

[1] Magic or Dunder Methods in Python: https://www.tutorialsteacher.com/python/magic-methods-in-python
[2] 3. Data model — Python 3.14.0 documentation: https://docs.python.org/3/reference/datamodel.html#specialnames
[3] Python's Magic Methods: Leverage Their Power in Your Classes – Real Python: https://realpython.com/python-magic-methods/

 

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区