Python的魔法方法是Python面向对象编程的精髓,理解魔法方法不仅能让开发者写出更优雅的代码,更重要的是能让你深入理解Python对象模型的工作原理。那么今天我们就来系统性的梳理一下Python魔法方法的基础知识、常用魔法方法与分类,以及应用场景实践。
什么是魔法方法? 想象一下这样的场景:当你写下 len([1, 2, 3]) 时,Python是如何知道要返回3的?当你使用 + 连接两个字符串时,背后发生了什么?答案就藏在魔法方法中。
1 2 3 4 print (len ([1 , 2 , 3 ])) print ("hello" + "world" ) print (str (42 ))
魔法方法是Python中以双下划线开头和结尾的特殊方法,也称为双下划线方法(如__init__、__str__、__new__等)。它们是Python中的一种协议机制,也是面向对象编程的核心机制之一,赋予了开发者自定义类行为的能力,让自定义类能像内置类型一样与Python语法无缝集成。
了解并理解魔法方法 对于我们Pythoner来说,不要把它们看作简单的”特殊方法”,而应该理解为:
是协议的实现 :魔法方法是Python鸭子类型的核心体现
是语言的扩展点 :通过它们可以扩展Python语言本身的能力
是语法的委托机制 :Python的语法很大程度上委托给对象本身来定义 正如Python之禅所说:”Python应该提供一种方法,但最好是只有一种明显的方法来做这件事。”魔法方法正是这种哲学的完美体现。
魔法方法的特点与分类 魔法方法是由Python解释器在特定场景下自动调用,无需显示调用,例如当你使用len(obj) 时,Python会调用对象的__len__方法。 由于魔法方法种类繁多,根据功能可以分为以下几类:
对象生命周期管理
对象表示与格式化
运算符重载
属性访问控制
容器与迭代器
同(异)步上下文管理
可调用对象与描述符
一些不常见但很有用的用法
下面将针对常用的场景进行展开分析
魔法方法在不同场景中的分析与实践 场景1:对象生命周期管理 下列这些魔法方法控制对象的创建,初始化和销毁。
__new__(cls, *args, **kwargs):控制对象的创建,是真正的构造器 ,是对象创建过程中的第一个方法(静态方法),它在__init__之前调用。通常情况下,是不需要直接使用__new__,但如果你需要控制对象的创建过程(如:实现单例模式或自定义元类),可以重写它。
__init__(self, *args, **kwargs):是初始化器 ,初始化对象的,对象在内存已经分配,即self已经存在,接收参数并设置初始状态,它在每次创建对象时被自动调用,几乎每个类都会实现它。
__del__(self):定义对象被垃圾回收时的行为。需要注意的是,最好谨慎使用,如果使用不当,可能引发资源管理问题。因为该方法不保证一定被调用,其主要依赖垃圾回收机制的策略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Resource : def __new__ (cls, *args, **kwargs ): """在__init__之前调用""" print ("__new__: 创建对象实例" ) instance = super ().__new__(cls) return instance def __init__ (self, name ): """设置对象初始状态""" print (f"__init__: 初始化资源 '{name} '" ) self .name = name self ._resource = self ._acquire_resource() def __del__ (self ): """对象被垃圾回收时调用""" print (f"__del__: 清理资源 '{self.name} '" ) self ._release_resource() def _acquire_resource (self ): print ("获取系统资源..." ) return "模拟的资源句柄" def _release_resource (self ): print ("释放系统资源..." ) resource = Resource("数据库连接" )
场景2:对象表示与格式化 下列这些魔法方法控制对象的字符串表示,用于调试或展示。
__str__(self):为最终用户设计,要求可读性好,定义str(obj)或print(obj)时的行为,也就是常被用于输出或打印,这个方法应该返回一个易于理解的字符串,用于展示对象的“外观”。
__repr__(self):为开发者设计,要求明确无误,定义repr(obj)或交互环境中对象的表示,通常用于开发者调试,返回详细信息,它的目标是生成一个可以通过eval()恢复的字符串(即反向构造对象)。
__format__(self, format_spec):定义format(obj, spec)或f-string 中的格式化的行为,支持自定义格式化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Student : def __init__ (self, name, age ): self .name = name self .age = age def __repr__ (self ): """这是对象的官方字符串表示,主要给开发者看""" return f"Student(name='{self.name} ', age={self.age} )" def __str__ (self ): """这是对象的非正式字符串表示,主要给用户看""" return f"{self.name} ,{self.age} 岁" ssw = Student("BluesSen" , 32 ) print (repr (tom)) print (str (tom)) print (tom)
场景3:属性访问控制 下列这些魔法方法控制对象的访问、设置和删除行为,适合实现动态属性或代理模式。
__getattr__(self, name):当访问不存在的属性时调用,适合实现灵活的属性延迟加载或代理模式,比如实现一个ORM模型。
__setattr__(self, name, value):设置属性时调用,但需要注意避免无限递归的问题,实际开发中建议使用 super().__setattr__ 来避免递归。
__delattr__(self, name):删除属性时调用。
__getattribute__(self, name):无条件访问任何属性时都会调用。极其强大,也极其危险,容易引发无限递归(必须通过super().__getattribute__来访问属性)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class LazyObject : def __init__ (self ): self .exists = 5 def __getattr__ (self, name ): """只有在找不到已存在的属性时才会调用""" print (f"__getattr__被调用,尝试获取不存在的属性: '{name} '" ) value = f"这是动态生成的属性 '{name} ' 的值" setattr (self , name, value) return value obj = LazyObject() print (obj.exists) print (obj.foo) print (obj.foo)
场景4:容器与迭代器 首先需要明确的什么是容器、可迭代对象、迭代器对象:
容器 :详见Python中的数据类型用法剖析:从底层实现到高效应用
可迭代对象 :实现 __iter__,返回迭代器对象(通常 return self)
迭代器对象 :实现 __iter__ 和 __next__,返回下一个值,结束时抛出 StopIteration。 下列这些魔法方法让类支持容器操作(如in、索引)和迭代。
__len__(self):定义len(obj)的行为。
__getitem__(self, key):可以实现自定义对象的下标访问行为,但需要注意的是在实现切片支持时,__getitem__需要处理slice对象。
__setitem__(self, key, value):可以实现自定义对象的下标赋值行为。
__delitem__(self, key) :用于实现删除通过键或索引访问的元素的操作。
__iter__(self)、__next__(self) :使对象可迭代(即支持for循环),这二者可与生成器结合优化内存。
__contains__(self, item) :定义 in 操作符的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class MyList : def __init__ (self, items ): self .items = list (items) def __len__ (self ): return len (self .items) def __getitem__ (self, index ): return self .items[index] def __iter__ (self ): return iter (self .items) def __contains__ (self, item ): return item in self .items ml = MyList([1 , 2 , 3 ]) print (len (ml)) print (ml[1 ]) print (2 in ml) for x in ml: print (x)
场景5:同步-上下文管理 下列这些魔法方法让类支持with语句,用于资源管理。
__enter__(self):进入with时调用,返回上下文对象。
__exit__(self, exc_type, exc_value, traceback):退出with时调用,处理异常,需要提醒的是,该方法返回True时可抑制异常,也就是说异常会被“吞掉”,上下文管理器外的代码不会收到异常。这是一个强大但危险的特性。很适合事务处理,如果结合contextlib模块可简化上下文管理器的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Timer : """一个计时器上下文管理器""" def __enter__ (self ): import time self .start = time.time() return self def __exit__ (self, exc_type, exc_val, exc_tb ): import time self .elapsed = time.time() - self .start print (f"代码块运行耗时: {self.elapsed:.4 f} 秒" ) return False with Timer() as t: sum (i for i in range (1000000 )) print (f"外部访问耗时: {t.elapsed} 秒" )
场景6:异步-上下文管理 下列这些魔法方法必须定义在支持异步的类中。
__await__(self)(可等待对象):必须返回一个迭代器(通常通过 iter() 包装一个生成器),可用于实现自定义可等待对象(如 asyncio.Future)。
__alter__(self)(异步迭代器):返回异步迭代器对象,通常return self。
__anext__:必须返回一个awaitable 对象,(通常是 coroutine 或 Task)。
__aenter__(self)(异步上下文管理器):返回进入时的资源,常为await self.connect().
__aexit__(self, exc_type, exc_value, traceback):用于异步清理(如关闭连接),可处理异常
StopAsyncIteration 是异步迭代结束的信号(不要手动抛出,除非你控制迭代逻辑)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import asyncioclass AsyncDatabase : async def connect (self ): print ("正在连接数据库..." ) await asyncio.sleep(1 ) print ("数据库连接成功" ) async def disconnect (self ): print ("正在断开数据库..." ) await asyncio.sleep(0.5 ) print ("数据库已断开" ) def __aenter__ (self ): return self .connect() def __aexit__ (self, exc_type, exc_value, traceback ): return self .disconnect() async def main (): async with AsyncDatabase() as db: print ("数据库操作中..." ) asyncio.run(main()) class AsyncCounter : def __init__ (self, limit ): self .limit = limit self .current = 0 def __aiter__ (self ): return self async def __anext__ (self ): if self .current < self .limit: await asyncio.sleep(0.1 ) self .current += 1 return self .current else : raise StopAsyncIteration async def main (): async for num in AsyncCounter(5 ): print (num) asyncio.run(main())
场景7:可调用对象 下列魔法方法让普通对象变为可调用对象,可用于实现函数式接口、装饰器类、回调等。
__call__(self, *args, **kwargs) :让类的实例像函数一样被调用,适合实现装饰器或函数式接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Counter : """一个计数器,每次调用都会递增""" def __init__ (self, start=0 ): self .count = start def __call__ (self ): """使得实例可以像函数一样被调用:obj()""" self .count += 1 print (f"当前计数: {self.count} " ) return self .count counter = Counter(10 ) counter() counter() class CallLogger : """记录函数调用信息的装饰器类""" def __init__ (self, func ): self .func = func self .call_count = 0 def __call__ (self, *args, **kwargs ): self .call_count += 1 print (f"开始执行第 {self.call_count} 次调用: {self.func.__name__} " ) result = self .func(*args, **kwargs) print (f"函数 {self.func.__name__} 执行完毕" ) return result @CallLogger def say_hello (name ): print (f"Hello, {name} !" ) say_hello("Alice" )
场景8:描述符协议 下列这些魔法方法,用于创建描述符类,控制属性的访问,本质是属性代理。简单地说,它允许一个对象在作为另一个对象的属性时,自定义其获取、设置、删除行为。常被用于属性验证 、懒加载 、ORM 字段 等场景。
__get__(self, instance, owner):获取属性值。
__set__(self, instance, value):设置属性值。
__delete__(self, instance) :输出属性。
__set_name__(self, owner, name):在类创建时设置描述符名称(Python3.6+)值得一提 :它们是 @property、@classmethod等装饰器的底层实现。在Django中被广泛使用。需要注意 :描述符是实现了__get__、__set__或__delete__中至少一个方法的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class PositiveNumber : """一个描述符,确保数值是正数""" def __init__ (self, name ): self .name = name def __get__ (self, instance, owner ): return instance.__dict__.get(self .name, 0 ) def __set__ (self, instance, value ): if value <= 0 : raise ValueError("值必须是正数" ) instance.__dict__[self .name] = value class Rectangle : width = PositiveNumber('width' ) height = PositiveNumber('height' ) def __init__ (self, w, h ): self .width = w self .height = h @property def area (self ): return self .width * self .height r = Rectangle(5 , 3 ) print (r.area) r.width = 10 print (r.area)
上述代码分析 :
描述符PositiveNumber的实例作为类属性(width, height)被赋值给Rectangle类。
当对实例r的属性(如r.width)进行访问或赋值时,实际上被拦截,转而调用描述符的__get__或__set__方法。
@property本质上就是一个实现了__get__和__set__的描述符。 总结:描述符是实现精细属性管理、ORM框架映射关系等高级功能的利器。
场景9:运算符重载 下列这些魔法方法允许自定义类支持Python的运算符(如:+、-、<等)。 (一) 实现算术运算符:
__add__(self, other):实现加法运算符 +。
__sub__(self, other):实现减法运算符 -。
__mul__(self, other):实现乘法运算符 *。
__truediv__(self, other):实现真除法运算符 /。
__floordiv__(self, other):实现整数除法运算符 //。
__mod__(self, other):实现取模运算符 %。
__pow__(self, other[, modulo]):实现幂运算符 **。
__iadd__(self, other):实现复合运算符 +=
__isub__(self, other):实现复合运算符 -=
__imul__(self, other) :实现复合运算符 *= (二)实现位运算符(不常用)
__lshift__(self, other):实现左移位运算符 <<。
__rshift__(self, other):实现右移位运算符 >>。
__and__(self, other):实现按位与运算符 &。
__xor__(self, other):实现按位异或运算符 ^。
__or__(self, other):实现按位或运算符 |。 (三)实现比较运算符:
__lt__(self, other):实现小于运算符 <。
__le__(self, other):实现小于等于运算符 <=。
__eq__(self, other):实现等于运算符 ==。
__ne__(self, other):实现不等于运算符 !=。
__gt__(self, other):实现大于运算符 >。
__ge__(self, other):实现大于等于运算符 >=。 **友情提醒:**实现比较运算符时,建议使用 functools.total_ordering 装饰器,只需定义 __eq__ 和 __lt__ 即可自动生成其他比较方法。
(四)实现容器类型类似方法(如序列、映射等):
这些方法允许对象像列表或字典一样被操作。
__getitem__(self, key):实现通过键或索引获取元素,如 self[key]。
__setitem__(self, key, value):实现通过键或索引设置元素,如 self[key] = value。
__delitem__(self, key):实现通过键或索引删除元素,如 del self[key]。
__contains__(self, item):实现成员测试运算符 in。
__len__(self):实现内置函数 len(),返回容器的长度。 由于方法太多,这里就简单的实现一个计算器功能吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Vector : def __init__ (self, x=0 , y=0 ): self .x = x self .y = y def __repr__ (self ): return f"Vector({self.x} , {self.y} )" def __add__ (self, other ): """实现 v1 + v2 或 v1 + number""" if isinstance (other, Vector): return Vector(self .x + other.x, self .y + other.y) elif isinstance (other, (int , float )): return Vector(self .x + other, self .y + other) else : return NotImplemented def __radd__ (self, other ): """实现 number + v1(当数字在左边时调用)""" return self .__add__(other) def __iadd__ (self, other ): """实现 v1 += v2 或 v1 += number(就地修改)""" if isinstance (other, Vector): self .x += other.x self .y += other.y elif isinstance (other, (int , float )): self .x += other self .y += other else : return NotImplemented return self v1 = Vector(1 , 2 ) v2 = Vector(3 , 4 ) print (v1 + v2) print (v1 + 5 ) print (5 + v1) v1 += v2 print (v1)
场景10:一些不常见但很有用的方法
__slots__ :限制类可添加的属性,优化内存和性能。
__class__ :获取或修改对象的类,动态性极强。
__copy__(self):copy.copy(obj),浅拷贝
__deepcopy__(self, memo):copy.deepcopy(obj),深拷贝
__getstate__(self):pickle 序列化时获取对象状态
_setstate__(self, state):pickle 反序列化时恢复状态
__dir__(self):dir(obj),自定义属性列表
__sizeof__(self):sys.getsizeof(obj),返回对象内存大小,
__hash__(self):hash(obj),用于哈希表(如字典键),
__instancecheck__(self, instance)、__subclasscheck__(self, subclass) :自定义 isinstance 和 issubclass 行为,通常在元类中使用。
一些开箱即用的实践案例 案例1:实现一个像列表和字典一样的类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Playlist : """一个简单的播放列表类""" def __init__ (self, *songs ): self ._songs = list (songs) def __len__ (self ): """实现 len(obj)""" return len (self ._songs) def __getitem__ (self, index ): """实现 obj[index],还支持切片!""" return self ._songs[index] def __contains__ (self, song ): """实现 item in obj""" return song in self ._songs def __iter__ (self ): """实现迭代,支持 for 循环""" return iter (self ._songs) my_playlist = Playlist("Song A" , "Song B" , "Song C" ) print (len (my_playlist)) print (my_playlist[1 ]) print (my_playlist[0 :2 ]) print ("Song B" in my_playlist) for song in my_playlist: print (song)
案例2:实现一个支持对象支持比较类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Student : def __init__ (self, name, age ): self .name = name self .age = age def __repr__ (self ): return f"Student(name='{self.name} ', age={self.age} )" def __eq__ (self, other ): """定义相等性:姓名和年龄都相同就认为是同一个学生""" if not isinstance (other, Student): return False return self .name == other.name and self .age == other.age def __hash__ (self ): """如果两个对象相等,它们的hash值必须相同""" return hash ((self .name, self .age)) tom1 = Student("Tom" , 20 ) tom2 = Student("Tom" , 20 ) print (tom1 == tom2) students = {tom1, tom2} print (len (students))
案:3:实现一个缓存器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 from functools import wrapsfrom collections import OrderedDictimport timeclass LRUCache : """LRU缓存实现""" def __init__ (self, maxsize=128 , ttl=None ): self .maxsize = maxsize self .ttl = ttl self .cache = OrderedDict() self .times = {} if ttl else None def __call__ (self, func ): @wraps(func ) def wrapper (*args, **kwargs ): key = self ._make_key(args, kwargs) if key in self .cache: if self ._is_valid(key): self .cache.move_to_end(key) return self .cache[key] else : self ._delete_key(key) result = func(*args, **kwargs) self ._store_result(key, result) return result wrapper.cache_info = self .cache_info wrapper.cache_clear = self .cache_clear return wrapper def _make_key (self, args, kwargs ): """创建缓存键""" key = args if kwargs: key += tuple (sorted (kwargs.items())) return key def _is_valid (self, key ): """检查缓存是否有效""" if self .ttl is None : return True return time.time() - self .times[key] < self .ttl def _delete_key (self, key ): """删除缓存项""" del self .cache[key] if self .times: del self .times[key] def _store_result (self, key, result ): """存储缓存结果""" if len (self .cache) >= self .maxsize: oldest_key = next (iter (self .cache)) self ._delete_key(oldest_key) self .cache[key] = result if self .times: self .times[key] = time.time() def cache_info (self ): """缓存信息""" return f"Cache size: {len (self.cache)} /{self.maxsize} " def cache_clear (self ): """清空缓存""" self .cache.clear() if self .times: self .times.clear() @LRUCache(maxsize=100 , ttl=300 ) def expensive_function (x, y ): time.sleep(1 ) return x * y + x ** 2
案例4:实现一个配置管理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 class Config : """支持点号访问和字典访问的配置管理器""" def __init__ (self, **kwargs ): self ._data = {} for key, value in kwargs.items(): self [key] = value def __getitem__ (self, key ): """字典式访问:config['database_url']""" return self ._data[key] def __setitem__ (self, key, value ): """字典式赋值:config['database_url'] = 'sqlite:///app.db'""" self ._data[key] = value def __delitem__ (self, key ): """删除配置项:del config['database_url']""" del self ._data[key] def __contains__ (self, key ): """检查是否存在:'database_url' in config""" return key in self ._data def __len__ (self ): """配置项数量:len(config)""" return len (self ._data) def __iter__ (self ): """迭代配置项:for key in config""" return iter (self ._data) def __getattr__ (self, name ): """点号访问:config.database_url""" try : return self ._data[name] except KeyError: raise AttributeError(f"配置项 '{name} ' 不存在" ) def __setattr__ (self, name, value ): """点号赋值:config.database_url = 'sqlite:///app.db'""" if name.startswith('_' ): super ().__setattr__(name, value) else : if not hasattr (self , '_data' ): super ().__setattr__('_data' , {}) self ._data[name] = value def __str__ (self ): items = [f"{k} ={v!r} " for k, v in self ._data.items()] return f"Config({', ' .join(items)} )" def keys (self ): return self ._data.keys() def values (self ): return self ._data.values() def items (self ): return self ._data.items() def get (self, key, default=None ): return self ._data.get(key, default) def update (self, **kwargs ): for key, value in kwargs.items(): self [key] = value config = Config( database_url="sqlite:///app.db" , debug=True , max_connections=100 ) print (config['database_url' ]) print (config.database_url) print ('debug' in config) config.secret_key = "my-secret-key" config['cache_timeout' ] = 300 for key in config: print (f"{key} : {config[key]} " ) print (f"共有 {len (config)} 个配置项" )
案例5:实现一个单例类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 class SingletonMeta (type ): """单例模式元类""" _instances = {} _lock = {} def __new__ (mcs, name, bases, namespace, **kwargs ): """创建新类时调用""" print (f"创建类 {name} " ) cls = super ().__new__(mcs, name, bases, namespace) mcs._lock[cls] = __import__ ('threading' ).Lock() return cls def __call__ (cls, *args, **kwargs ): """创建实例时调用""" if cls not in cls._instances: with cls._lock[cls]: if cls not in cls._instances: print (f"创建 {cls.__name__} 的第一个实例" ) instance = super ().__call__(*args, **kwargs) cls._instances[cls] = instance else : print (f"返回 {cls.__name__} 的现有实例" ) else : print (f"返回 {cls.__name__} 的现有实例" ) return cls._instances[cls] class DatabaseConnection (metaclass=SingletonMeta): """数据库连接类(单例)""" def __init__ (self, host="localhost" , port=5432 ): if hasattr (self , '_initialized' ): return self .host = host self .port = port self .connected = False self ._initialized = True print (f"初始化数据库连接: {host} :{port} " ) def connect (self ): if not self .connected: print (f"连接到数据库 {self.host} :{self.port} " ) self .connected = True else : print ("已经连接到数据库" ) def query (self, sql ): if not self .connected: self .connect() print (f"执行查询: {sql} " ) return f"查询结果 for '{sql} '" print ("=== 创建数据库连接实例 ===" )db1 = DatabaseConnection("192.168.1.100" ) db2 = DatabaseConnection("localhost" ) db3 = DatabaseConnection() print (f"db1 is db2: {db1 is db2} " ) print (f"db1.host: {db1.host} " ) db1.connect() db2.query("SELECT * FROM users" )
一些好的实践与建议
开发调试阶段 :
__repr__ 比 __str__ 更重要,调试时非常有用。
建议使用 :
实现 __eq__ 时,通常也要实现 __hash__(除非对象是可变的)。
使用 @total_ordering 装饰器可以减少比较方法的编写量(只需实现 __eq__ 和一个如 __lt__)。
在设计类时最好做到总是检查参数类型,使用NotImplemented而不是抛出异常。
在设计类时考虑使用@dataclass或@attrs来减少样板代码
魔法方法调用有开销,在性能敏感的循环中,直接调用方法可能比操作符更快。
谨慎使用:
在__getattribute__、__setattr__中直接使用self.attr会导致无限递归。必须使用super()或直接操作__dict__。
总结一下
类别
主要方法
应用场景
对象生命周期
__new__, __init__, __del__
对象创建、初始化、销毁
对象表示与格式化
__str__, __repr__, __format__
字符串表示、调试输出
运算符重载
__add__, __eq__, __lt__等
数学运算、比较操作
属性访问控制
__getattr__, __setattr__, __getattribute__
动态属性、代理模式
容器与迭代器
__len__, __getitem__, __iter__
序列、映射、迭代
上下文管理
__enter__, __exit__
资源管理、with语句
可调用对象
__call__
函数式接口、装饰器类
描述符协议
__get__, __set__, __delete__
属性验证、ORM字段
异步编程
__aenter__, __aexit__, __anext__
异步上下文、迭代
Python的魔法方法不是“黑魔法”,而是一套精心设计的、用于扩展语言能力的协议系统。作为Python开发者,深入理解并恰当地运用它们,能够让我们设计出API更清晰、更符合Python风格、更强大的代码库。
然而,常言道:能力越大,责任越大……
魔法方法虽然功能强大,但滥用魔法方法会让代码变得难以理解和调试。我们应该始终遵循“明确胜于隐晦”的Python之禅,只在真正需要让对象模拟内置行为或实现特定协议时才使用它们。