魔术方法是什么?(定义)

魔术方法(Magic Methods)是 Python 类中前后各有两个下划线的特殊方法(也叫 “双下划线方法”“Dunder Methods”),核心特点是:

  • 由 Python 自动调用,无需手动触发;
  • 用于实现类的特殊行为(如实例创建、字符串表示、比较、算术运算等),让类的行为更像内置类型。

分类详解与代码示例

1. 构造与初始化(实例生命周期)

核心方法
方法 作用 调用时机
__new__(cls, ...) 创建实例(分配内存空间),是类的 “静态方法”(第一个参数是 cls __init__ 之前执行
__init__(self, ...) 初始化实例属性(设置实例的初始状态) __new__ 返回实例后自动执行
__del__(self) 析构方法(实例被垃圾回收时调用) 实例被销毁时自动执行
代码示例
class Person:
    # __new__:创建实例(通常不需要重写,除非需要控制实例创建过程)
    def __new__(cls, name, age):
        print(f"__new__ 被调用:正在创建 {cls.__name__} 实例")
        # 调用父类(object)的 __new__ 方法创建实例
        return super().__new__(cls)
    
    # __init__:初始化实例属性(最常用)
    def __init__(self, name, age):
        print(f"__init__ 被调用:正在初始化 {name} 的属性")
        self.name = name  # 实例属性
        self.age = age
    
    # __del__:析构方法(不推荐手动使用,依赖垃圾回收时机)
    def __del__(self):
        print(f"__del__ 被调用:{self.name} 实例被销毁")

# 创建实例(自动调用 __new__ → __init__)
p = Person("Tom", 18)
# 输出:
# __new__ 被调用:正在创建 Person 实例
# __init__ 被调用:正在初始化 Tom 的属性

# 手动删除实例(触发 __del__,但实际开发中不依赖此方法)
del p
# 输出:__del__ 被调用:Tom 实例被销毁
面试重点:__new__ vs __init__
  • __new__ 负责 “创建实例”(返回一个新对象);
  • __init__ 负责 “初始化实例”(对 __new__ 返回的对象设置属性,无返回值)。

2. 字符串表示(让类的实例 “可读”)

核心方法
方法 作用 触发场景
__str__(self) 用户友好的字符串表示(给人看的) str(obj)print(obj)
__repr__(self) 开发者友好的字符串表示(给程序看的,目标是能重建实例) repr(obj)、交互式解释器
代码示例
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    # __str__:给用户看的简洁描述
    def __str__(self):
        return f"Person(name={self.name}, age={self.age})"
    
    # __repr__:给开发者看的详细描述(通常包含重建实例的代码)
    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

p = Person("Tom", 18)

print(str(p))   # 输出:Person(name=Tom, age=18)(调用 __str__)
print(repr(p))  # 输出:Person('Tom', 18)(调用 __repr__)
print(p)        # 输出:Person(name=Tom, age=18)(print 默认调用 __str__)
面试重点
  • 如果只定义了 __repr____str__ 会默认使用 __repr__ 的返回值;
  • 建议至少定义 __repr__,保证实例在任何场景下都有清晰的表示。

3. 比较操作(让实例支持 ==<> 等)

核心方法
方法 对应运算符 作用
__eq__(self, other) == 判断 “等于”
__ne__(self, other) != 判断 “不等于”(默认继承 __eq__ 的反逻辑)
__lt__(self, other) < 判断 “小于”
__gt__(self, other) > 判断 “大于”
__le__(self, other) <= 判断 “小于等于”
__ge__(self, other) >= 判断 “大于等于”
代码示例
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    # __eq__:判断两个实例是否“相等”(按 name 和 age 比较)
    def __eq__(self, other):
        if not isinstance(other, Person):
            return False  # 不是 Person 类型,直接返回 False
        return self.name == other.name and self.age == other.age
    
    # __lt__:判断“小于”(按 age 比较)
    def __lt__(self, other):
        if not isinstance(other, Person):
            raise TypeError("只能与 Person 类型比较")
        return self.age < other.age

p1 = Person("Tom", 18)
p2 = Person("Tom", 18)
p3 = Person("Jerry", 20)

print(p1 == p2)  # 输出:True(调用 __eq__)
print(p1 < p3)   # 输出:True(调用 __lt__)
print(p1 > p3)   # 输出:False(Python 会自动用 __lt__ 的反逻辑推导)

4. 算术运算(让实例支持 +-* 等)

方法 对应运算符 作用
__add__(self, other) + 加法
__sub__(self, other) - 减法
__mul__(self, other) * 乘法
__truediv__(self, other) / 真除法(Python3)
__floordiv__(self, other) // 地板除法
__mod__(self, other) % 取模
__pow__(self, other) ** 幂运算
反向运算(当左侧对象不支持该运算时调用)
方法 对应场景
__radd__(self, other) other + self(左侧不是当前类实例)
__rsub__(self, other) other - self
代码示例
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    # __add__:向量加法(+)
    def __add__(self, other):
        if not isinstance(other, Vector):
            raise TypeError("只能与 Vector 相加")
        return Vector(self.x + other.x, self.y + other.y)
    
    # __mul__:向量数乘(*)
    def __mul__(self, scalar):
        if not isinstance(scalar, (int, float)):
            raise TypeError("只能与数字相乘")
        return Vector(self.x * scalar, self.y * scalar)
    
    # __rmul__:反向数乘(当左侧是数字时调用,如 2 * v)
    def __rmul__(self, scalar):
        return self.__mul__(scalar)  # 复用 __mul__ 的逻辑
    
    # __str__:方便打印
    def __str__(self):
        return f"Vector({self.x}, {self.y})"

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

print(v1 + v2)    # 输出:Vector(4, 6)(调用 __add__)
print(v1 * 3)     # 输出:Vector(3, 6)(调用 __mul__)
print(3 * v1)     # 输出:Vector(3, 6)(调用 __rmul__,因为 3 是 int,没有 __mul__ 方法处理 Vector)

5. 容器操作(让类像列表 / 字典一样工作)

核心方法
方法 对应操作 作用
__len__(self) len(obj) 获取长度
__getitem__(self, key) obj[key] 获取元素
__setitem__(self, key, value) obj[key] = value 设置元素
__delitem__(self, key) del obj[key] 删除元素
__contains__(self, item) item in obj 成员判断
__iter__(self) for item in obj 迭代(返回迭代器)
代码示例:自定义列表
class MyList:
    def __init__(self, data=None):
        self.data = data if data is not None else []
    
    # __len__:支持 len()
    def __len__(self):
        return len(self.data)
    
    # __getitem__:支持 obj[key] 获取元素
    def __getitem__(self, index):
        return self.data[index]
    
    # __setitem__:支持 obj[key] = value 设置元素
    def __setitem__(self, index, value):
        self.data[index] = value
    
    # __contains__:支持 item in obj
    def __contains__(self, item):
        return item in self.data
    
    # __iter__:支持 for 循环迭代
    def __iter__(self):
        return iter(self.data)  # 直接返回 data 的迭代器

ml = MyList([1, 2, 3, 4])

print(len(ml))       # 输出:4(调用 __len__)
print(ml[0])         # 输出:1(调用 __getitem__)
ml[0] = 100          # 调用 __setitem__
print(ml[0])         # 输出:100
print(2 in ml)       # 输出:True(调用 __contains__)

for item in ml:      # 调用 __iter__
    print(item, end=" ")  # 输出:100 2 3 4

6. 上下文管理(支持 with 语句,自动管理资源)

核心方法
方法 作用
__enter__(self) 进入 with 块前执行,返回值会赋值给 as 后的变量(可选)。
__exit__(self, exc_type, exc_val, exc_tb) 离开 with 块时执行(无论是否发生异常),用于资源清理。参数:- exc_type:异常类型(无异常时为 None)- exc_val:异常值- exc_tb:异常追踪信息
代码示例:自定义文件管理器
class MyFile:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
    
    # __enter__:进入 with 块时执行,返回文件对象
    def __enter__(self):
        print(f"打开文件:{self.filename}")
        self.file = open(self.filename, self.mode)
        return self.file  # 返回值给 as 后的变量
    
    # __exit__:离开 with 块时执行,关闭文件
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"关闭文件:{self.filename}")
        self.file.close()
        # 如果返回 True,表示异常已处理,不向外抛出;返回 None/False 则继续抛出
        if exc_type:
            print(f"捕获异常:{exc_val}")
            return True

# 使用 with 语句(自动调用 __enter__ 和 __exit__)
with MyFile("test.txt", "w") as f:
    f.write("Hello, Magic Methods!")
    # 故意触发异常,测试 __exit__ 的异常处理
    # raise ValueError("测试异常")

7. 属性访问(控制实例属性的获取、设置、删除)

核心方法
方法 作用 触发场景
__getattr__(self, name) 访问不存在的属性时调用 obj.nonexistent_attr
__getattribute__(self, name) 访问任何属性时调用(优先级最高,需谨慎使用,避免无限递归) 访问任何属性
__setattr__(self, name, value) 设置属性时调用 obj.attr = value
__delattr__(self, name) 删除属性时调用 del obj.attr
代码示例
class Person:
    def __init__(self, name):
        self.name = name  # 会调用 __setattr__
    
    # __getattr__:只在访问“不存在”的属性时调用
    def __getattr__(self, name):
        return f"⚠️ 属性 '{name}' 不存在"
    
    # __setattr__:设置任何属性时调用
    def __setattr__(self, name, value):
        print(f"设置属性:{name} = {value}")
        # 注意:不能直接用 self.name = value,会无限递归!
        # 必须用 super().__setattr__ 或 self.__dict__[name] = value
        super().__setattr__(name, value)
    
    # __delattr__:删除属性时调用
    def __delattr__(self, name):
        print(f"删除属性:{name}")
        super().__delattr__(name)

p = Person("Tom")
# 输出:设置属性:name = Tom

print(p.name)        # 输出:Tom(存在的属性,不调用 __getattr__)
print(p.age)         # 输出:⚠️ 属性 'age' 不存在(不存在的属性,调用 __getattr__)

p.age = 18           # 输出:设置属性:age = 18(调用 __setattr__)
del p.age            # 输出:删除属性:age(调用 __delattr__)
面试重点
  • __getattr__ 只在属性不存在时调用;
  • __getattribute__ 会拦截所有属性访问,使用时必须通过 super().__getattribute__(name) 获取属性,否则会无限递归。

8. 可调用对象(让实例像函数一样被调用)

核心方法
方法 作用 触发场景
__call__(self, ...) 让实例可以像函数一样被调用 obj(...)
代码示例:计数器
class Counter:
    def __init__(self):
        self.count = 0
    
    # __call__:让实例可调用,每次调用 count +1
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"已调用 {self.count} 次")
        return self.count

c = Counter()

c()  # 输出:已调用 1 次(调用 __call__)
c()  # 输出:已调用 2 次
c()  # 输出:已调用 3 次

9. 类型转换(让实例支持 int()float()bool() 等)

核心方法
方法 对应操作 作用
__int__(self) int(obj) 转换为整数
__float__(self) float(obj) 转换为浮点数
__bool__(self) bool(obj) 转换为布尔值(用于 if 判断)
__str__(self) str(obj) 转换为字符串(前面已讲)
代码示例
class MyNumber:
    def __init__(self, value):
        self.value = value
    
    def __int__(self):
        return int(self.value)
    
    def __float__(self):
        return float(self.value)
    
    def __bool__(self):
        return self.value > 0  # 大于 0 为 True,否则为 False

n = MyNumber(3.14)

print(int(n))    # 输出:3(调用 __int__)
print(float(n))  # 输出:3.14(调用 __float__)
print(bool(n))   # 输出:True(调用 __bool__)

if n:  # 自动调用 __bool__
    print("n 是正数")

总结

魔术方法是 Python 面向对象编程的 “灵魂”,通过重写它们,你可以让类的行为完全符合你的预期。面试中重点掌握:

  • 构造初始化:__new____init__
  • 字符串表示:__str____repr__
  • 上下文管理:__enter____exit__
  • 容器操作:__len____getitem__
  • 可调用对象:__call__
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐