一、什么是魔法方法?
魔法方法(Magic Methods)是Python中一种特殊的方法,它们以双下划线(__
)开头和结尾(如__init__
、__str__
等)。魔法方法允许你定义类在特定情况下的行为,例如初始化、字符串表示、运算符重载等。
二、常见的魔法方法分类
1. 构造和初始化
-
__new__(cls, [...])
: 创建实例时调用的第一个方法 -
__init__(self, [...])
: 实例初始化方法 -
__del__(self)
: 析构方法,当实例被销毁时调用
2. 字符串表示
-
__str__(self)
: 定义str()
和print()
的行为 -
__repr__(self)
: 定义repr()
的行为,通常用于调试 -
__format__(self, format_spec)
: 定义format()
的行为
3. 比较运算符
-
__eq__(self, other)
: 定义==
行为 -
__ne__(self, other)
: 定义!=
行为 -
__lt__(self, other)
: 定义<
行为 -
__le__(self, other)
: 定义<=
行为 -
__gt__(self, other)
: 定义>
行为 -
__ge__(self, other)
: 定义>=
行为
4. 算术运算符
-
__add__(self, other)
:+
-
__sub__(self, other)
:-
-
__mul__(self, other)
:*
-
__truediv__(self, other)
:/
-
__floordiv__(self, other)
://
-
__mod__(self, other)
:%
-
__pow__(self, other)
:**
5. 容器类型方法
-
__len__(self)
: 定义len()
行为 -
__getitem__(self, key)
: 定义self[key]
行为 -
__setitem__(self, key, value)
: 定义self[key] = value
行为 -
__delitem__(self, key)
: 定义del self[key]
行为 -
__contains__(self, item)
: 定义in
操作符行为
6. 可调用对象
-
__call__(self, [...])
: 使实例可以像函数一样被调用
7. 上下文管理
-
__enter__(self)
: 定义with
语句开始时的行为 -
__exit__(self, exc_type, exc_val, exc_tb)
: 定义with
语句结束时的行为
三、魔法方法练习题
1. __init__
- 构造初始化方法
这是最常见的魔法方法,用于初始化新创建的对象。
class Dog:
def __init__(self, name, age):
self.name = name # 设置实例属性
self.age = age
print(f"一只名叫{name}的狗出生了!")
# 创建实例时会自动调用__init__
my_dog = Dog("旺财", 3) # 输出:一只名叫旺财的狗出生了!
print(my_dog.name) # 输出:旺财
2. __str__
vs __repr__
- 字符串表示
-
__str__
:给用户看的友好字符串 -
__repr__
:给开发者看的准确字符串(通常可以用来重建对象)class Point: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"这是一个点({self.x}, {self.y})" def __repr__(self): return f"Point({self.x}, {self.y})" p = Point(1, 2) print(p) # 输出:这是一个点(1, 2) print(str(p)) # 输出:这是一个点(1, 2) print(repr(p)) # 输出:Point(1, 2)
3. 算术运算符
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 __sub__(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(2, 3)
v2 = Vector(1, 1)
print(v1 + v2) # Vector(3, 4)
print(v1 - v2) # Vector(1, 2)
print(v1 * 3) # Vector(6, 9)
4. 比较运算符
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
# 等于 ==
def __eq__(self, other):
return self.score == other.score
# 大于 >
def __gt__(self, other):
return self.score > other.score
# 小于等于 <=
def __le__(self, other):
return self.score <= other.score
alice = Student("Alice", 85)
bob = Student("Bob", 75)
print(alice == bob) # False
print(alice > bob) # True
print(alice <= bob) # False
5. __len__
- 获取长度
class Playlist:
def __init__(self, songs):
self.songs = list(songs)
def __len__(self):
return len(self.songs)
my_playlist = Playlist(["Song1", "Song2", "Song3"])
print(len(my_playlist)) # 输出:3
6. __getitem__
和 __setitem__
- 索引访问
class Playlist:
def __init__(self, songs):
self.songs = list(songs)
def __getitem__(self, index):
return self.songs[index]
def __setitem__(self, index, value):
self.songs[index] = value
pl = Playlist(["A", "B", "C"])
print(pl[1]) # 输出:B
pl[1] = "New Song"
print(pl[1]) # 输出:New Song
7. __iter__
- 使对象可迭代
class CountDown:
def __init__(self, start):
self.start = start
def __iter__(self):
current = self.start
while current > 0:
yield current
current -= 1
for num in CountDown(5):
print(num) # 输出:5 4 3 2 1
8. __call__
- 使实例可调用
class Adder:
def __init__(self, n):
self.n = n
def __call__(self, x):
return self.n + x
add5 = Adder(5)
print(add5(3)) # 输出:8
print(add5(10)) # 输出:15
9. __enter__
和 __exit__
- 上下文管理
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.end = time.time()
print(f"耗时: {self.end - self.start:.2f}秒")
with Timer():
# 模拟耗时操作
sum(i for i in range(1000000))
# 输出:耗时: 0.12秒
四、综合练习案例
实现一个简单的分数类
class Fraction:
def __init__(self, numerator, denominator=1):
self.numerator = numerator
self.denominator = denominator
self._simplify()
def _simplify(self):
"""约分分数"""
def gcd(a, b):
while b:
a, b = b, a % b
return a
common_divisor = gcd(self.numerator, self.denominator)
self.numerator //= common_divisor
self.denominator //= common_divisor
def __add__(self, other):
new_num = self.numerator * other.denominator + other.numerator * self.denominator
new_den = self.denominator * other.denominator
return Fraction(new_num, new_den)
def __sub__(self, other):
new_num = self.numerator * other.denominator - other.numerator * self.denominator
new_den = self.denominator * other.denominator
return Fraction(new_num, new_den)
def __mul__(self, other):
return Fraction(self.numerator * other.numerator,
self.denominator * other.denominator)
def __truediv__(self, other):
return Fraction(self.numerator * other.denominator,
self.denominator * other.numerator)
def __eq__(self, other):
return (self.numerator == other.numerator and
self.denominator == other.denominator)
def __str__(self):
return f"{self.numerator}/{self.denominator}"
def __repr__(self):
return f"Fraction({self.numerator}, {self.denominator})"
# 使用示例
f1 = Fraction(1, 2)
f2 = Fraction(1, 3)
print(f1 + f2) # 输出:5/6
print(f1 - f2) # 输出:1/6
print(f1 * f2) # 输出:1/6
print(f1 / f2) # 输出:3/2
print(f1 == f2) # 输出:False
五、魔法方法总结
-
核心概念:
-
魔法方法是Python中特殊的方法,用于自定义类的行为
-
它们以双下划线开头和结尾
-
Python在特定情况下自动调用这些方法
-
-
主要用途:
-
初始化对象(
__init__
,__new__
) -
字符串表示(
__str__
,__repr__
) -
运算符重载(
__add__
,__sub__
等) -
容器行为(
__len__
,__getitem__
等) -
上下文管理(
__enter__
,__exit__
) -
可调用对象(
__call__
)
-
-
最佳实践:
-
__repr__
应该返回一个可以用来重建对象的字符串 -
__str__
应该返回一个用户友好的字符串 -
实现比较方法时保持一致性
-
运算符重载应该保持直观的行为
-
-
注意事项:
-
不要滥用魔法方法,保持行为直观
-
某些魔法方法需要成对实现(如
__eq__
和__hash__
) -
继承内置类型时要小心,可能需要重写多个魔法方法
-
-
学习建议:
-
从简单的
__init__
和__str__
开始 -
逐步尝试运算符重载
-
理解Python的数据模型如何工作
-
查看内置类型的源代码学习标准实现
-
-
魔法方法总结表
魔法方法 | 描述 | 触发方式 |
---|---|---|
__init__(self) | 对象初始化 | obj = Class() |
__str__(self) | 用户友好字符串表示 | str(obj) , print(obj) |
__repr__(self) | 开发者字符串表示 | repr(obj) |
__add__(self, other) | 加法运算 + | obj1 + obj2 |
__sub__(self, other) | 减法运算 - | obj1 - obj2 |
__mul__(self, other) | 乘法运算 * | obj1 * obj2 |
__eq__(self, other) | 等于比较 == | obj1 == obj2 |
__lt__(self, other) | 小于比较 < | obj1 < obj2 |
__len__(self) | 获取长度 | len(obj) |
__getitem__(self, key) | 索引访问 | obj[key] |
__setitem__(self, key, value) | 索引赋值 | obj[key] = value |
__iter__(self) | 迭代支持 | for x in obj |
__call__(self, ...) | 使实例可调用 | obj() |
__enter__(self) , __exit__(self) | 上下文管理 | with obj: |
记住这些魔法方法的关键是理解它们何时被Python自动调用。通过实现这些方法,你可以让你自定义的类表现得像Python内置类型一样自然。