Python魔法方法:解锁对象的超能力
想让你创建的对象像Python内置对象一样优雅、智能吗?那就必须掌握这些神奇的双下划线方法!
引言:为什么需要魔法方法?
想象一下,你创建了一个对象,它不能进行加法运算(obj1 + obj2),不能像字典一样使用索引(obj[key]),甚至打印出来都是一堆看不懂的内存地址…多么无趣的对象啊!
幸运的是,Python为我们提供了一套“魔法方法”——那些以双下划线开头和结尾的特殊方法。它们就像是你对象的“超能力”,让普通对象瞬间变身超级对象!
第一章:生死轮回——对象的创建与销毁
每个对象都有它的生命周期,从诞生到消亡,都有魔法方法相伴。
__new__:对象的“产房”
class UpperStr(str): """创建一个总是大写的字符串类""" def __new__(cls, string): string = string.upper() return super().__new__(cls, string)
normal_str = str("hello") magic_str = UpperStr("hello")
|
重要提醒:__new__是静态方法(但不用加@staticmethod),它在__init__之前被调用,负责“制造”对象本身。
__del__:对象的“临终遗言”
class MemorableObject: def __init__(self, name): self.name = name def __del__(self): print(f"『{self.name}』对象:我走了,正如我轻轻地来...")
obj = MemorableObject("短暂的生命") del obj
|
注意:__del__就像对象的遗言,但说遗言的时间由垃圾回收器决定,可能不会立刻执行。不要用它来清理重要资源(比如关闭文件),用with语句或显式的close()方法更好。
第二章:算数超能力——让对象会数学
想让你的对象支持加减乘除吗?简单!
class Vector2D: """二维向量类""" def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): """定义加法:向量相加""" return Vector2D(self.x + other.x, self.y + other.y) def __sub__(self, other): """定义减法""" return Vector2D(self.x - other.x, self.y - other.y) def __mul__(self, scalar): """定义乘法:向量乘以标量""" return Vector2D(self.x * scalar, self.y * scalar) def __repr__(self): return f"Vector2D({self.x}, {self.y})"
v1 = Vector2D(1, 2) v2 = Vector2D(3, 4)
print(v1 + v2) print(v2 - v1) print(v1 * 3)
|
小知识:
__add__、__sub__等对应+、-运算符
__radd__是右加法,当左操作数不支持加法时使用
__iadd__是原地加法,对应+=运算符
第三章:属性守卫——精细控制属性访问
想给你的属性加个“保安”吗?这些方法能帮你!
class User: """一个对年龄有严格要求的用户类""" def __init__(self, name, age): self.name = name self._age = age def __setattr__(self, name, value): """属性赋值时的守卫""" if name == "_age": if not 0 <= value <= 150: raise ValueError("年龄必须在0到150岁之间!") if value < 18: print("警告:未成年人 detected!") super().__setattr__(name, value) def __getattr__(self, name): """访问不存在的属性时的处理""" if name == "is_adult": return self._age >= 18 elif name.startswith("default_"): return f"这是{name}的默认值" else: raise AttributeError(f"'{self.__class__.__name__}'没有属性'{name}'") def __delattr__(self, name): """删除属性时的处理""" if name == "_age": raise AttributeError("年龄是重要信息,不能删除!") super().__delattr__(name)
user = User("小明", 25) print(user.is_adult)
|
重要区别:
__getattribute__:访问任何属性时都触发(包括存在的属性)
__getattr__:只在访问不存在的属性时触发(最后的防线)
第四章:序列化超能力——像列表一样操作
想让你的对象支持索引和切片吗?安排!
class Playlist: """一个音乐播放列表""" def __init__(self, songs): self.songs = list(songs) def __getitem__(self, index): """支持索引和切片""" if isinstance(index, slice): return Playlist(self.songs[index]) return self.songs[index] def __setitem__(self, index, value): """设置歌曲时自动添加后缀""" if isinstance(index, slice): self.songs[index] = [f"🎵 {song} 🎵" for song in value] else: self.songs[index] = f"🎵 {value} 🎵" def __len__(self): """支持len()函数""" return len(self.songs) def __repr__(self): return f"Playlist({self.songs})"
my_playlist = Playlist(["晴天", "夜曲", "七里香"]) print(my_playlist[0]) print(my_playlist[1:]) print(len(my_playlist))
my_playlist[0] = "稻香" print(my_playlist[0])
|
第五章:迭代的艺术——让对象可循环
想让你的对象能在for循环中使用吗?需要两个魔法方法!
class Countdown: """倒计时迭代器""" def __init__(self, start): self.current = start def __iter__(self): """返回迭代器对象""" return self def __next__(self): """返回下一个值""" if self.current <= 0: raise StopIteration value = self.current self.current -= 1 return value
for num in Countdown(5): print(num, end=" ")
|
第六章:关系测试——判断元素是否存在
class LotteryNumbers: """彩票号码池""" def __init__(self, numbers): self.numbers = set(numbers) def __contains__(self, number): """支持in操作符""" return number in self.numbers
lottery = LotteryNumbers([3, 7, 11, 23, 42]) print(7 in lottery) print(99 in lottery)
|
代偿机制:如果类没有实现__contains__,Python会尝试使用__iter__,如果还没有,会尝试__getitem__。这就是Python的”代偿”机制,总能找到一种方式满足你的需求!
第七章:伪装成函数——可调用对象
想让对象像函数一样被调用吗?只需一个魔法方法!
class Multiplier: """一个可以记住倍数的乘法器""" def __init__(self, factor): self.factor = factor def __call__(self, number): """让对象可以像函数一样被调用""" return number * self.factor
double = Multiplier(2) triple = Multiplier(3)
print(double(5)) print(triple(5)) print(double(10))
|
第八章:字符串变身术——美化对象展示
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): """给人看的字符串(简洁友好)""" return f"{self.name}, {self.age}岁" def __repr__(self): """给开发者看的字符串(详细准确)""" return f"Person('{self.name}', {self.age})"
person = Person("张三", 30)
print(person) print(str(person)) print(repr(person))
|
重要提示:如果只实现了__repr__,Python会用它来代偿__str__。但反过来不行,所以至少要实现__repr__。
总结:魔法方法的力量
通过魔法方法,你可以:
- 控制对象的生命周期(
__new__、__init__、__del__)
- 赋予对象运算能力(
__add__、__sub__等)
- 精细管理属性访问(
__getattr__、__setattr__等)
- 让对象支持索引和切片(
__getitem__、__setitem__)
- 使对象可迭代(
__iter__、__next__)
- 支持成员关系测试(
__contains__)
- 让对象可调用(
__call__)
- 美化对象显示(
__str__、__repr__)
掌握这些魔法方法,你的Python对象就能从”普通对象”升级为”超级对象”,拥有和Python内置对象一样优雅的行为和强大的功能!
记住,能力越大,责任越大。合理使用魔法方法,让你的代码既强大又优雅!