016_Python 魔术方法(特殊方法)学习笔记

内容分享4小时前发布 吉3to3
0 1 0

魔术方法(Magic Methods)以__开头和结尾,是 Python 实现面向对象特性的核心机制。它们由解释器自动调用,无需手动触发,用于赋予对象特殊行为(如运算符重载、容器模拟、上下文管理等)。

一、构造与初始化相关

方法名称

触发条件

主要应用场景

__new__(cls)

创建实例时最先调用(在__init__之前),返回实例对象

自定义实例创建逻辑(如单例模式、不可变对象初始化)

__init__(self)

实例创建后初始化调用,接收参数并赋值给实例属性

初始化实例属性,设置对象初始状态

__del__(self)

实例被垃圾回收时调用(析构函数)

释放资源(如关闭文件、数据库连接),注意:不保证必定会被及时调用

示例

python

运行

class Singleton:
    _instance = None
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        self.value = 0

s1 = Singleton()
s2 = Singleton()
print(s1 is s2)  # True(单例模式)

二、字符串表明相关

方法名称

触发条件

主要应用场景

__str__(self)

print(obj)、str(obj)时调用

定义对象的 “友善字符串表明”(面向用户)

__repr__(self)

repr(obj)、交互式解释器中输出对象时调用

定义对象的 “官方字符串表明”(面向开发者,要求可还原对象)

示例

python

运行

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"Person({self.name}, {self.age})"  # 用户友善
    
    def __repr__(self):
        return f"Person(name='{self.name}', age={self.age})"  # 可还原对象

p = Person("Alice", 25)
print(p)          # Person(Alice, 25)(__str__)
print(repr(p))    # Person(name='Alice', age=25)(__repr__)

三、运算符重载相关

1. 算术运算符

方法名称

触发条件

主要应用场景

__add__(self, other)

self + other

自定义加法逻辑

__sub__(self, other)

self – other

自定义减法逻辑

__mul__(self, other)

self * other

自定义乘法逻辑

__truediv__(self, other)

self / other

自定义除法逻辑

__mod__(self, other)

self % other

自定义取模逻辑

2. 比较运算符

方法名称

触发条件

主要应用场景

__eq__(self, other)

self == other

自定义相等判断

__ne__(self, other)

self != other

自定义不等判断

__lt__(self, other)

self < other

自定义小于判断

__gt__(self, other)

self > other

自定义大于判断

__le__(self, other)

self <= other

自定义小于等于判断

__ge__(self, other)

self >= other

自定义大于等于判断

示例

python

运行

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)   # <__main__.Vector object at 0x...>(需配合__str__)
print(v1 == Vector(1,2))  # True

四、容器类型模拟

用于让自定义对象像列表、字典等容器一样工作。

方法名称

触发条件

主要应用场景

__len__(self)

len(obj)时调用

定义容器的 “长度” 计算逻辑

__getitem__(self, key)

obj[key]时调用(获取元素)

支持索引 / 键访问元素(如列表的obj[0]、字典的obj['key'])

__setitem__(self, key, value)

obj[key] = value时调用(设置元素)

支持索引 / 键赋值

__delitem__(self, key)

del obj[key]时调用(删除元素)

支持删除指定索引 / 键的元素

__contains__(self, item)

item in obj时调用

支持in运算符判断元素是否存在

示例

python

运行

class CustomList:
    def __init__(self):
        self.data = []
    
    def __len__(self):
        return len(self.data)
    
    def __getitem__(self, idx):
        return self.data[idx]
    
    def __setitem__(self, idx, value):
        self.data[idx] = value
    
    def __contains__(self, item):
        return item in self.data

cl = CustomList()
cl.data = [1,2,3]
print(len(cl))    # 3
print(cl[0])      # 1
cl[0] = 10
print(2 in cl)    # True

五、可调用对象模拟

方法名称

触发条件

主要应用场景

__call__(self, *args, **kwargs)

将实例作为函数调用(obj()或obj(args))

让对象具备 “函数行为”(如装饰器、状态保持的可调用对象)

示例

python

运行

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(调用实例时更新状态)

六、上下文管理器(with 语句)

方法名称

触发条件

主要应用场景

__enter__(self)

进入with语句块时调用,返回上下文对象

初始化资源(如打开文件、连接数据库)

__exit__(self, exc_type, exc_val, exc_tb)

退出with语句块时调用,接收异常信息(若有)

释放资源(如关闭文件、回滚事务),处理异常

示例

python

运行

class FileHandler:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
    
    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()

with FileHandler("test.txt", "w") as f:
    f.write("Hello")  # 自动打开/关闭文件

七、属性访问控制

用于自定义属性的获取、设置、删除逻辑。

方法名称

触发条件

主要应用场景

__getattr__(self, name)

访问不存在的属性时调用(obj.name)

动态生成属性、处理属性不存在的情况

__getattribute__(self, name)

访问任意属性时优先调用(覆盖默认属性查找)

全局控制属性访问(需谨慎使用,避免递归)

__setattr__(self, name, value)

设置属性时调用(obj.name = value)

验证属性赋值、拦截属性设置

__delattr__(self, name)

删除属性时调用(del obj.name)

控制属性删除逻辑

示例

python

运行

class SafeDict:
    def __init__(self):
        self._data = {}
    
    def __getattr__(self, name):
        return self._data.get(name, f"属性{name}不存在")
    
    def __setattr__(self, name, value):
        if name.startswith("_"):
            super().__setattr__(name, value)  # 避免递归
        else:
            self._data[name] = value

sd = SafeDict()
sd.name = "Alice"
print(sd.name)    # Alice
print(sd.age)     # 属性age不存在

八、迭代器相关

用于让对象支持迭代(如for…in循环)。

方法名称

触发条件

主要应用场景

__iter__(self)

调用iter(obj)时触发,返回迭代器对象

定义对象的迭代逻辑,返回自身或其他迭代器

__next__(self)

调用next(obj)时触发,返回下一个元素

实现迭代器的 “下一个元素” 获取逻辑,无元素时抛出StopIteration

示例

python

运行

class MyRange:
    def __init__(self, start, end):
        self.start = start
        self.end = end
    
    def __iter__(self):
        return self  # 返回自身作为迭代器
    
    def __next__(self):
        if self.start >= self.end:
            raise StopIteration
        current = self.start
        self.start += 1
        return current

for i in MyRange(1, 4):
    print(i)  # 1 2 3

九、其他常用魔术方法

方法名称

触发条件

主要应用场景

__hash__(self)

调用hash(obj)时触发,返回哈希值

让对象可作为字典的键、集合的元素(需配合__eq__)

__bool__(self)

调用bool(obj)或判断对象真假时触发(如if obj:)

定义对象的 “真假” 判断逻辑

__copy__(self)

调用copy.copy(obj)时触发

实现浅拷贝逻辑

__deepcopy__(self, memo)

调用copy.deepcopy(obj)时触发

实现深拷贝逻辑

示例

python

运行

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __hash__(self):
        return hash((self.x, self.y))  # 基于坐标生成哈希值
    
    def __bool__(self):
        return self.x != 0 or self.y != 0  # 坐标全0时为False

p1 = Point(1, 2)
p2 = Point(0, 0)
print(hash(p1))  # 哈希值
print(bool(p1))  # True
print(bool(p2))  # False

总结

魔术方法是 Python 面向对象的 “黑魔法”,通过实现这些方法,能让自定义对象拥有 Python 内置类型的行为特性。核心要点:

  1. 魔术方法由解释器自动调用,无需手动调用;
  2. 不同魔术方法对应不同的场景(如运算符、容器、上下文管理);
  3. 合理使用魔术方法可大幅提升代码的简洁性和可读性,让自定义类更 “Pythonic”。
© 版权声明

相关文章

1 条评论

  • 头像
    Lllllrrr_ 投稿者

    收藏了,感谢分享

    无记录
    回复