Python“魔法方法”入门:从`__init__`到`__str__`
目录
- Python“魔法方法”入门:从`__init__`到`__str__`
-
- 1. 引言:什么是魔法方法?
-
- 1.1 魔法方法的重要性
- 1.2 一个简单的例子
- 2. 构造与初始化方法
-
- 2.1 `__new__` vs `__init__`
- 2.2 `__init__`方法的深入理解
- 2.3 `__del__`:析构方法
- 3. 对象表示方法
-
- 3.1 `__str__` vs `__repr__`
- 3.2 何时使用哪种表示方法
- 4. 比较运算魔法方法
-
- 4.1 基本比较方法
- 4.2 比较运算的数学原理
- 5. 算术运算魔法方法
-
- 5.1 基本算术运算
- 5.2 算术运算的数学原理
- 6. 类型转换魔法方法
-
- 6.1 基本类型转换方法
- 7. 容器类魔法方法
-
- 7.1 实现一个自定义列表
- 8. 完整示例:一个完整的数学向量类
-
- 代码自查与说明
- 9. 总结
-
- 9.1 主要收获
- 9.2 进一步学习
『宝藏代码胶囊开张啦!』—— 我的 CodeCapsule 来咯!✨
写代码不再头疼!我的新站点 CodeCapsule 主打一个 “白菜价”+“量身定制”!无论是卡脖子的毕设/课设/文献复现,需要灵光一现的算法改进,还是想给项目加个“外挂”,这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网
Python“魔法方法”入门:从__init__到__str__
1. 引言:什么是魔法方法?
在Python中,有一类特殊的方法,它们以双下划线开头和结尾,被称为魔法方法(Magic Methods)或双下方法(Dunder Methods)。这些方法不是让我们直接调用的,而是由Python解释器在特定时机自动调用。
1.1 魔法方法的重要性
魔法方法是Python面向对象编程的核心。它们使得我们可以:
- 自定义对象的初始化过程
- 定义对象如何被打印和显示
- 重载运算符(如+、-、*、/等)
- 让对象支持上下文管理器(with语句)
- 实现自定义的容器行为
1.2 一个简单的例子
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person(name='{self.name}', age={self.age})"
# 使用示例
person = Person("Alice", 25)
print(person) # 自动调用__str__方法
输出:
Person(name='Alice', age=25)
2. 构造与初始化方法
2.1 __new__ vs __init__
很多人混淆这两个方法,但它们有明确的职责分工:
class Example:
def __new__(cls, *args, **kwargs):
"""创建实例,返回实例对象"""
print("__new__被调用")
instance = super().__new__(cls)
return instance
def __init__(self, value):
"""初始化实例,设置初始状态"""
print("__init__被调用")
self.value = value
# 测试
obj = Example(10)
输出:
__new__被调用
__init__被调用
关键区别:
__new__:类方法,负责创建实例__init__:实例方法,负责初始化实例
2.2 __init__方法的深入理解
__init__是最常用的魔法方法,用于对象的初始化:
class Vector:
def __init__(self, x, y, z=0):
"""
初始化三维向量
参数:
x: x坐标
y: y坐标
z: z坐标(默认为0)
"""
self.x = x
self.y = y
self.z = z
self.magnitude = (x**2 + y**2 + z**2) ** 0.5
# 使用示例
v1 = Vector(3, 4) # 二维向量,z默认为0
v2 = Vector(1, 2, 3) # 三维向量
print(f"v1: ({v1.x}, {v1.y}, {v1.z}), 模长: {v1.magnitude:.2f}")
print(f"v2: ({v2.x}, {v2.y}, {v2.z}), 模长: {v2.magnitude:.2f}")
2.3 __del__:析构方法
当对象被垃圾回收时调用:
class Resource:
def __init__(self, name):
self.name = name
print(f"资源 {self.name} 被创建")
def __del__(self):
print(f"资源 {self.name} 被释放")
def test_del():
res = Resource("临时资源")
# 函数结束时,res超出作用域,触发__del__
test_del()
3. 对象表示方法
3.1 __str__ vs __repr__
这两个方法都用于对象的字符串表示,但用途不同:
class Student:
def __init__(self, name, student_id, grades=None):
self.name = name
self.student_id = student_id
self.grades = grades or []
def __str__(self):
"""用户友好的字符串表示"""
avg_grade = sum(self.grades) / len(self.grades) if self.grades else 0
return f"学生{self.name}(学号:{self.student_id}), 平均分:{avg_grade:.1f}"
def __repr__(self):
"""开发者友好的字符串表示,应该能够用于重新创建对象"""
return f"Student('{self.name}', '{self.student_id}', {self.grades})"
def add_grade(self, grade):
"""添加成绩"""
self.grades.append(grade)
# 测试
student = Student("张三", "2023001", [85, 92, 78])
student.add_grade(88)
print(str(student)) # 调用__str__
print(repr(student)) # 调用__repr__
# 在交互式环境中,直接输入变量名会调用__repr__
输出:
学生张三(学号:2023001), 平均分:85.8
Student('张三', '2023001', [85, 92, 78, 88])
3.2 何时使用哪种表示方法
下面的流程图说明了Python如何选择使用__str__还是__repr__:
graph TD
A[需要对象的字符串表示] --> B{使用场景是什么?}
B -->|print(obj) <br> str(obj) <br> f-string格式化| C[优先使用 __str__]
B -->|交互式环境直接输入obj <br> repr(obj) <br> 容器中显示| D[使用 __repr__]
C --> E{__str__ 已定义?}
D --> F{__repr__ 已定义?}
E -->|是| G[调用 __str__]
E -->|否| H[回退到 __repr__]
F -->|是| I[调用 __repr__]
F -->|否| J[使用默认对象表示<br>如<__main__.Student object>]
G --> K[返回友好字符串]
H --> I
I --> L[返回精确字符串]
4. 比较运算魔法方法
Python允许我们重载比较运算符,让自定义对象支持比较操作。
4.1 基本比较方法
class Fraction:
"""分数类"""
def __init__(self, numerator, denominator=1):
if denominator == 0:
raise ValueError("分母不能为零")
# 简化分数
gcd_val = self._gcd(abs(numerator), abs(denominator))
self.numerator = numerator // gcd_val
self.denominator = denominator // gcd_val
# 确保分母为正
if self.denominator < 0:
self.numerator = -self.numerator
self.denominator = -self.denominator
def _gcd(self, a, b):
"""计算最大公约数"""
while b:
a, b = b, a % b
return a
def to_float(self):
"""转换为浮点数"""
return self.numerator / self.denominator
# 比较运算方法
def __eq__(self, other):
"""等于 =="""
if isinstance(other, Fraction):
return (self.numerator == other.numerator and
self.denominator == other.denominator)
return self.to_float() == other
def __lt__(self, other):
"""小于 <"""
if isinstance(other, Fraction):
return (self.numerator * other.denominator <
other.numerator * self.denominator)
return self.to_float() < other
def __le__(self, other):
"""小于等于 <="""
return self < other or self == other
def __gt__(self, other):
"""大于 >"""
return not self <= other
def __ge__(self, other):
"""大于等于 >="""
return not self < other
def __ne__(self, other):
"""不等于 !="""
return not self == other
def __str__(self):
if self.denominator == 1:
return str(self.numerator)
return f"{self.numerator}/{self.denominator}"
def __repr__(self):
return f"Fraction({self.numerator}, {self.denominator})"
# 测试比较运算
f1 = Fraction(1, 2)
f2 = Fraction(2, 4) # 等于1/2
f3 = Fraction(3, 4)
print(f"{f1} == {f2}: {f1 == f2}") # True
print(f"{f1} < {f3}: {f1 < f3}") # True
print(f"{f3} > {f1}: {f3 > f1}") # True
print(f"{f1} == 0.5: {f1 == 0.5}") # True
4.2 比较运算的数学原理
当我们比较两个分数 a b \frac{a}{b} ba 和 c d \frac{c}{d} dc 时:
- 相等性比较: a b = c d ⟺ a × d = b × c \frac{a}{b} = \frac{c}{d} \iff a \times d = b \times c ba=dc⟺a×d=b×c
- 大小比较: a b < c d ⟺ a × d < b × c \frac{a}{b} < \frac{c}{d} \iff a \times d < b \times c ba<dc⟺a×d<b×c(假设 b , d > 0 b, d > 0 b,d>0)
这种方法避免了浮点数精度问题,直接使用整数运算进行比较。
5. 算术运算魔法方法
5.1 基本算术运算
让我们扩展Fraction类来支持算术运算:
class Fraction:
# ... 之前的代码保持不变 ...
# 算术运算方法
def __add__(self, other):
"""加法 +"""
if isinstance(other, Fraction):
new_num = (self.numerator * other.denominator +
other.numerator * self.denominator)
new_den = self.denominator * other.denominator
return Fraction(new_num, new_den)
elif isinstance(other, (int, float)):
return self + Fraction(other)
return NotImplemented
def __radd__(self, other):
"""右加法,处理 other + self 的情况"""
return self + other
def __sub__(self, other):
"""减法 -"""
if isinstance(other, Fraction):
new_num = (self.numerator * other.denominator -
other.numerator * self.denominator)
new_den = self.denominator * other.denominator
return Fraction(new_num, new_den)
elif isinstance(other, (int, float)):
return self - Fraction(other)
return NotImplemented
def __rsub__(self, other):
"""右减法"""
return Fraction(other) - self
def __mul__(self, other):
"""乘法 *"""
if isinstance(other, Fraction):
return Fraction(self.numerator * other.numerator,
self.denominator * other.denominator)
elif isinstance(other, (int, float)):
return Fraction(self.numerator * other, self.denominator)
return NotImplemented
def __rmul__(self, other):
"""右乘法"""
return self * other
def __truediv__(self, other):
"""除法 /"""
if isinstance(other, Fraction):
return self * Fraction(other.denominator, other.numerator)
elif isinstance(other, (int, float)):
return self * Fraction(1, other)
return NotImplemented
def __rtruediv__(self, other):
"""右除法"""
return Fraction(other) / self
def __floordiv__(self, other):
"""整除 //"""
result = self / other
return Fraction(int(result.to_float()), 1)
def __mod__(self, other):
"""取模 %"""
if isinstance(other, Fraction):
quotient = self // other
return self - quotient * other
return NotImplemented
def __pow__(self, exponent):
"""幂运算 **"""
if isinstance(exponent, int):
return Fraction(self.numerator ** exponent,
self.denominator ** exponent)
return NotImplemented
# 测试算术运算
f1 = Fraction(1, 2)
f2 = Fraction(3, 4)
print(f"{f1} + {f2} = {f1 + f2}") # 1/2 + 3/4 = 5/4
print(f"{f1} - {f2} = {f1 - f2}") # 1/2 - 3/4 = -1/4
print(f"{f1} * {f2} = {f1 * f2}") # 1/2 * 3/4 = 3/8
print(f"{f1} / {f2} = {f1 / f2}") # 1/2 ÷ 3/4 = 2/3
print(f"{f1} ** 2 = {f1 ** 2}") # (1/2)² = 1/4
# 测试反向运算
print(f"2 + {f1} = {2 + f1}") # 2 + 1/2 = 5/2
print(f"3 * {f1} = {3 * f1}") # 3 * 1/2 = 3/2
5.2 算术运算的数学原理
分数运算遵循标准的数学规则:
- 加法: a b + c d = a d + b c b d \frac{a}{b} + \frac{c}{d} = \frac{ad + bc}{bd} ba+dc=bdad+bc
- 减法: a b − c d = a d − b c b d \frac{a}{b} - \frac{c}{d} = \frac{ad - bc}{bd} ba−dc=bdad−bc
- 乘法: a b × c d = a c b d \frac{a}{b} \times \frac{c}{d} = \frac{ac}{bd} ba×dc=bdac
- 除法: a b ÷ c d = a b × d c = a d b c \frac{a}{b} \div \frac{c}{d} = \frac{a}{b} \times \frac{d}{c} = \frac{ad}{bc} ba÷dc=ba×cd=bcad
6. 类型转换魔法方法
6.1 基本类型转换方法
class SmartNumber:
"""智能数字类,支持各种类型转换"""
def __init__(self, value):
self.value = float(value)
# 类型转换方法
def __int__(self):
"""转换为整数"""
return int(self.value)
def __float__(self):
"""转换为浮点数"""
return self.value
def __bool__(self):
"""转换为布尔值"""
return self.value != 0
def __complex__(self):
"""转换为复数"""
return complex(self.value, 0)
# 数值表示方法
def __str__(self):
return f"SmartNumber({self.value})"
def __repr__(self):
return f"SmartNumber({self.value})"
# 算术运算(简化版)
def __add__(self, other):
if isinstance(other, (int, float)):
return SmartNumber(self.value + other)
return NotImplemented
# 测试类型转换
num = SmartNumber(3.14)
print(f"整数: {int(num)}") # 3
print(f"浮点数: {float(num)}") # 3.14
print(f"布尔值: {bool(num)}") # True
print(f"复数: {complex(num)}") # (3.14+0j)
print(f"零值的布尔: {bool(SmartNumber(0))}") # False
7. 容器类魔法方法
7.1 实现一个自定义列表
class StatisticsList:
"""统计列表,增强的列表类,支持统计功能"""
def __init__(self, initial_data=None):
self._data = list(initial_data) if initial_data else []
# 基本容器方法
def __getitem__(self, index):
"""支持索引访问"""
return self._data[index]
def __setitem__(self, index, value):
"""支持索引赋值"""
self._data[index] = value
def __delitem__(self, index):
"""支持删除元素"""
del self._data[index]
def __len__(self):
"""返回列表长度"""
return len(self._data)
def __contains__(self, item):
"""支持in操作符"""
return item in self._data
def __iter__(self):
"""支持迭代"""
return iter(self._data)
def append(self, item):
"""添加元素"""
self._data.append(item)
# 统计方法
def mean(self):
"""计算平均值"""
if not self._data:
return 0
return sum(self._data) / len(self._data)
def median(self):
"""计算中位数"""
if not self._data:
return 0
sorted_data = sorted(self._data)
n = len(sorted_data)
mid = n // 2
if n % 2 == 0:
return (sorted_data[mid-1] + sorted_data[mid]) / 2
else:
return sorted_data[mid]
def mode(self):
"""计算众数"""
if not self._data:
return []
from collections import Counter
counter = Counter(self._data)
max_count = max(counter.values())
return [num for num, count in counter.items() if count == max_count]
def variance(self):
"""计算方差"""
if len(self._data) < 2:
return 0
mean_val = self.mean()
return sum((x - mean_val) ** 2 for x in self._data) / (len(self._data) - 1)
def std_dev(self):
"""计算标准差"""
return self.variance() ** 0.5
def __str__(self):
return f"StatisticsList({self._data})"
def __repr__(self):
return f"StatisticsList({self._data})"
# 测试统计列表
stats_list = StatisticsList([1, 2, 2, 3, 4, 5, 5, 5])
print(f"列表: {stats_list}")
print(f"长度: {len(stats_list)}")
print(f"平均值: {stats_list.mean():.2f}")
print(f"中位数: {stats_list.median()}")
print(f"众数: {stats_list.mode()}")
print(f"方差: {stats_list.variance():.2f}")
print(f"标准差: {stats_list.std_dev():.2f}")
# 测试容器功能
print(f"包含3: {3 in stats_list}")
print(f"索引2的值: {stats_list[2]}")
8. 完整示例:一个完整的数学向量类
现在让我们将所有知识整合起来,创建一个功能完整的数学向量类:
import math
from numbers import Real
class Vector:
"""
数学向量类,支持基本的向量运算
实现二维和三维向量操作
"""
def __init__(self, *components):
"""
初始化向量
参数:
*components: 向量的分量,支持2D或3D向量
"""
if not components:
raise ValueError("向量必须至少有一个分量")
if not all(isinstance(c, Real) for c in components):
raise TypeError("向量分量必须是实数")
self._components = tuple(float(c) for c in components)
self._dim = len(components)
@property
def dim(self):
"""返回向量维度"""
return self._dim
@property
def components(self):
"""返回向量分量(只读)"""
return self._components
@property
def magnitude(self):
"""计算向量模长"""
return math.sqrt(sum(x**2 for x in self._components))
@property
def unit_vector(self):
"""返回单位向量"""
mag = self.magnitude
if mag == 0:
raise ValueError("零向量没有单位向量")
return Vector(*(x / mag for x in self._components))
# 表示方法
def __str__(self):
comp_str = ", ".join(f"{x:.2f}" for x in self._components)
return f"Vector({comp_str})"
def __repr__(self):
comp_str = ", ".join(str(x) for x in self._components)
return f"Vector({comp_str})"
# 比较运算
def __eq__(self, other):
"""向量相等性判断"""
if not isinstance(other, Vector):
return False
return (self._dim == other._dim and
self._components == other._components)
def __abs__(self):
"""返回向量的模长"""
return self.magnitude
# 算术运算
def __add__(self, other):
"""向量加法"""
if isinstance(other, Vector):
if self._dim != other._dim:
raise ValueError("向量维度必须相同才能相加")
return Vector(*(a + b for a, b in zip(self._components, other._components)))
return NotImplemented
def __sub__(self, other):
"""向量减法"""
if isinstance(other, Vector):
if self._dim != other._dim:
raise ValueError("向量维度必须相同才能相减")
return Vector(*(a - b for a, b in zip(self._components, other._components)))
return NotImplemented
def __mul__(self, other):
"""向量数乘或点积"""
if isinstance(other, Real):
# 数乘
return Vector(*(x * other for x in self._components))
elif isinstance(other, Vector):
# 点积
if self._dim != other._dim:
raise ValueError("向量维度必须相同才能计算点积")
return sum(a * b for a, b in zip(self._components, other._components))
return NotImplemented
def __rmul__(self, other):
"""右数乘"""
if isinstance(other, Real):
return self * other
return NotImplemented
def __truediv__(self, other):
"""向量数除"""
if isinstance(other, Real):
if other == 0:
raise ZeroDivisionError("不能除以零")
return Vector(*(x / other for x in self._components))
return NotImplemented
def __matmul__(self, other):
"""向量叉积(使用@运算符)"""
if isinstance(other, Vector):
if self._dim != 3 or other._dim != 3:
raise ValueError("叉积只支持三维向量")
x1, y1, z1 = self._components
x2, y2, z2 = other._components
return Vector(
y1 * z2 - z1 * y2,
z1 * x2 - x1 * z2,
x1 * y2 - y1 * x2
)
return NotImplemented
def dot(self, other):
"""点积(方法形式)"""
return self * other
def cross(self, other):
"""叉积(方法形式)"""
return self @ other
def angle_between(self, other):
"""计算两向量夹角(弧度)"""
if self.magnitude == 0 or other.magnitude == 0:
raise ValueError("不能计算零向量的夹角")
dot_product = self.dot(other)
cos_theta = dot_product / (self.magnitude * other.magnitude)
# 处理浮点数精度问题
cos_theta = max(-1.0, min(1.0, cos_theta))
return math.acos(cos_theta)
def is_orthogonal(self, other, tolerance=1e-10):
"""判断两向量是否正交"""
return abs(self.dot(other)) < tolerance
def is_parallel(self, other, tolerance=1e-10):
"""判断两向量是否平行"""
if self.magnitude == 0 or other.magnitude == 0:
return True # 零向量与任何向量平行
cos_theta = abs(self.dot(other)) / (self.magnitude * other.magnitude)
return abs(cos_theta - 1.0) < tolerance
# 容器方法
def __getitem__(self, index):
"""支持索引访问分量"""
if index < 0 or index >= self._dim:
raise IndexError("向量索引越界")
return self._components[index]
def __len__(self):
"""返回向量维度"""
return self._dim
def __iter__(self):
"""支持迭代"""
return iter(self._components)
# 测试完整的向量类
def test_vector_class():
"""测试向量类的各种功能"""
print("=== 向量类测试 ===\n")
# 创建向量
v1 = Vector(1, 2, 3)
v2 = Vector(4, 5, 6)
print(f"v1 = {v1}")
print(f"v2 = {v2}")
print(f"v1的维度: {v1.dim}")
print(f"v1的模长: {v1.magnitude:.2f}")
print(f"v1的单位向量: {v1.unit_vector}")
# 向量运算
print(f"\n=== 向量运算 ===")
print(f"v1 + v2 = {v1 + v2}")
print(f"v2 - v1 = {v2 - v1}")
print(f"v1 * 2 = {v1 * 2}")
print(f"3 * v2 = {3 * v2}")
print(f"v1 / 2 = {v1 / 2}")
# 点积和叉积
print(f"\n=== 点积和叉积 ===")
print(f"v1 · v2 = {v1.dot(v2):.2f}")
print(f"v1 × v2 = {v1.cross(v2)}")
# 向量关系
print(f"\n=== 向量关系 ===")
v3 = Vector(2, 4, 6) # 与v1平行
v4 = Vector(1, -0.5, 0) # 与v1大致正交
print(f"v1 == v2: {v1 == v2}")
print(f"v1与v3平行: {v1.is_parallel(v3)}")
print(f"v1与v4正交: {v1.is_orthogonal(v4)}")
# 计算夹角
angle_rad = v1.angle_between(v2)
angle_deg = math.degrees(angle_rad)
print(f"v1与v2的夹角: {angle_rad:.2f} 弧度 ({angle_deg:.1f} 度)")
# 测试2D向量
print(f"\n=== 2D向量测试 ===")
v5 = Vector(3, 4)
print(f"2D向量v5: {v5}")
print(f"v5的模长: {v5.magnitude}")
if __name__ == "__main__":
test_vector_class()
代码自查与说明
-
数学正确性:
- 向量运算遵循数学定义
- 处理了边界情况(如零向量)
- 使用适当的数值稳定性技巧
-
代码规范:
- 完整的文档字符串
- 清晰的变量命名
- 适当的错误处理
-
魔法方法覆盖:
__init__: 对象初始化__str__/__repr__: 字符串表示- 比较运算方法
- 算术运算方法
- 容器协议方法
-
功能完整性:
- 支持2D和3D向量
- 实现所有基本向量运算
- 提供高级数学功能
这个向量类展示了如何综合运用各种魔法方法来创建一个功能完整、接口自然的数学类。
9. 总结
通过本文的学习,我们深入了解了Python魔法方法的核心概念:
9.1 主要收获
-
魔法方法的作用:让自定义类具有内置类型一样的行为
-
常用魔法方法分类:
- 构造初始化:
__init__,__new__,__del__ - 对象表示:
__str__,__repr__ - 比较运算:
__eq__,__lt__, 等 - 算术运算:
__add__,__sub__,__mul__, 等 - 类型转换:
__int__,__float__,__bool__ - 容器协议:
__getitem__,__setitem__,__len__
- 构造初始化:
-
最佳实践:
- 始终返回
NotImplemented而不是抛出TypeError - 实现反向运算方法(如
__radd__) - 区分
__str__和__repr__的用途 - 保持数学运算的准确性
- 始终返回
9.2 进一步学习
要深入学习魔法方法,建议:
- 阅读Python官方文档的数据模型章节
- 研究标准库中类的实现(如
collections模块) - 实践实现更复杂的魔法方法,如上下文管理器(
__enter__,__exit__)
掌握魔法方法是成为Python高级程序员的重要一步,它们让代码更加Pythonic和优雅。
更多推荐



所有评论(0)