本文目录导读:

- 字符串表示:
__str__和__repr__ - 比较操作:
__eq__,__lt__,__gt__等 - 容器行为:
__getitem__,__setitem__,__len__,__contains__ - 迭代器支持:
__iter__和__next__ - 上下文管理器:
__enter__和__exit__ - 运算符重载:
__add__,__sub__,__mul__等 - 属性访问控制:
__getattr__,__setattr__,__getattribute__ - 可调用对象:
__call__ - 完整案例:自定义字典类
在Python中,魔法方法(Magic Methods,也叫双下划线方法)能让我们自定义类的行为,使其更像Python内置类型,下面通过具体案例来说明魔法方法的常见应用场景。
字符串表示:__str__ 和 __repr__
class Book:
def __init__(self, title, author, price):
self.title = title
self.author = author
self.price = price
# 给用户看的友好字符串
def __str__(self):
return f"《{self.title}》- {self.author}"
# 给开发者看的无歧义字符串
def __repr__(self):
return f"Book('{self.title}', '{self.author}', {self.price})"
book = Book("Python编程", "张三", 59.0)
print(str(book)) # 《Python编程》- 张三
print(repr(book)) # Book('Python编程', '张三', 59.0)
比较操作:__eq__, __lt__, __gt__ 等
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
# 定义等于比较
def __eq__(self, other):
return self.score == other.score
# 定义小于比较
def __lt__(self, other):
return self.score < other.score
# 定义字符串表示便于打印
def __repr__(self):
return f"{self.name}: {self.score}"
students = [
Student("小明", 85),
Student("小红", 92),
Student("小刚", 78)
]
students.sort() # 自动使用 __lt__ 排序
print(students) # [小刚: 78, 小明: 85, 小红: 92]
print(students[0] == students[1]) # False,使用 __eq__
容器行为:__getitem__, __setitem__, __len__, __contains__
class Playlist:
def __init__(self):
self._songs = []
# 添加歌曲
def add_song(self, song):
self._songs.append(song)
# 获取长度
def __len__(self):
return len(self._songs)
# 索引访问
def __getitem__(self, index):
return self._songs[index]
# 索引赋值
def __setitem__(self, index, song):
self._songs[index] = song
# 包含检查
def __contains__(self, song):
return song in self._songs
playlist = Playlist()
playlist.add_song("告白气球")
playlist.add_song("七里香")
playlist.add_song("青花瓷")
print(len(playlist)) # 3,使用 __len__
print(playlist[1]) # 七里香,使用 __getitem__
playlist[1] = "稻香" # 使用 __setitem__
print("稻香" in playlist) # True,使用 __contains__
迭代器支持:__iter__ 和 __next__
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for num in CountDown(5):
print(num, end=" ") # 5 4 3 2 1
上下文管理器:__enter__ 和 __exit__
class FileManager:
def __init__(self, filename, mode='r'):
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()
# 返回 False 会传播异常,True 会抑制异常
return False
# 使用 with 语句自动管理资源
with FileManager('test.txt', 'w') as f:
f.write('Hello, Python!')
运算符重载:__add__, __sub__, __mul__ 等
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 __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
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__
属性访问控制:__getattr__, __setattr__, __getattribute__
class Config:
def __init__(self):
self.settings = {
'host': 'localhost',
'port': 8080
}
def __getattr__(self, name):
# 当常规属性查找失败时调用
if name in self.settings:
return self.settings[name]
raise AttributeError(f"Config has no attribute '{name}'")
def __setattr__(self, name, value):
# 拦截所有属性设置
if name == 'settings':
super().__setattr__(name, value)
else:
# 存储到 settings 字典中
if not hasattr(self, 'settings'):
self.settings = {}
self.settings[name] = value
config = Config()
print(config.host) # localhost,通过 __getattr__
config.port = 9090 # 通过 __setattr__ 存储
print(config.port) # 9090
可调用对象:__call__
class Logger:
def __init__(self, prefix):
self.prefix = prefix
def __call__(self, message, level='INFO'):
print(f"[{self.prefix}] [{level}] {message}")
logger = Logger("APP")
logger("应用启动") # [APP] [INFO] 应用启动
logger("错误发生", "ERROR") # [APP] [ERROR] 错误发生
完整案例:自定义字典类
class CaseInsensitiveDict:
"""键大小写不敏感的字典"""
def __init__(self, data=None):
self._data = {}
if data:
for key, value in data.items():
self[key] = value
def __setitem__(self, key, value):
self._data[key.lower()] = value
def __getitem__(self, key):
return self._data[key.lower()]
def __delitem__(self, key):
del self._data[key.lower()]
def __contains__(self, key):
return key.lower() in self._data
def __len__(self):
return len(self._data)
def __iter__(self):
return iter(self._data)
def __repr__(self):
return str(self._data)
# 使用示例
d = CaseInsensitiveDict({"Name": "Alice", "Age": 30})
print(d["NAME"]) # Alice,大小写不敏感
print(d["name"]) # Alice
d["AGE"] = 31 # 覆盖原有的 Age
print("age" in d) # True
print(len(d)) # 2
for key in d:
print(key, d[key]) # name Alice, age 31
魔法方法的应用核心价值:
- 让自定义类更像内置类型 - 支持
len()、in、 等操作 - 实现特定协议 - 迭代器、上下文管理器、数字类型等
- 控制对象行为 - 属性访问、运算符重载、字符串表示
- 简化使用接口 - 通过
__call__使对象可调用,通过__enter__/__exit__支持with语句
你在项目中遇到哪些需要自定义魔法方法的场景吗?我可以帮你分析如何实现。