当前位置: 首页 > news >正文

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 = nameself.age = agedef __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 instancedef __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 = xself.y = yself.z = zself.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 = nameprint(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 = nameself.student_id = student_idself.grades = grades or []def __str__(self):"""用户友好的字符串表示"""avg_grade = sum(self.grades) / len(self.grades) if self.grades else 0return 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 TDA[需要对象的字符串表示] --> 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>如&lt;__main__.Student object&gt;]G --> K[返回友好字符串]H --> II --> 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_valself.denominator = denominator // gcd_val# 确保分母为正if self.denominator < 0:self.numerator = -self.numeratorself.denominator = -self.denominatordef _gcd(self, a, b):"""计算最大公约数"""while b:a, b = b, a % breturn adef 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() == otherdef __lt__(self, other):"""小于 <"""if isinstance(other, Fraction):return (self.numerator * other.denominator < other.numerator * self.denominator)return self.to_float() < otherdef __le__(self, other):"""小于等于 <="""return self < other or self == otherdef __gt__(self, other):"""大于 >"""return not self <= otherdef __ge__(self, other):"""大于等于 >="""return not self < otherdef __ne__(self, other):"""不等于 !="""return not self == otherdef __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 比较运算的数学原理

当我们比较两个分数 ab\frac{a}{b}bacd\frac{c}{d}dc 时:

  • 相等性比较ab=cd⟺a×d=b×c\frac{a}{b} = \frac{c}{d} \iff a \times d = b \times cba=dca×d=b×c
  • 大小比较ab<cd⟺a×d<b×c\frac{a}{b} < \frac{c}{d} \iff a \times d < b \times cba<dca×d<b×c(假设 b,d>0b, d > 0b,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.denominatorreturn Fraction(new_num, new_den)elif isinstance(other, (int, float)):return self + Fraction(other)return NotImplementeddef __radd__(self, other):"""右加法,处理 other + self 的情况"""return self + otherdef __sub__(self, other):"""减法 -"""if isinstance(other, Fraction):new_num = (self.numerator * other.denominator - other.numerator * self.denominator)new_den = self.denominator * other.denominatorreturn Fraction(new_num, new_den)elif isinstance(other, (int, float)):return self - Fraction(other)return NotImplementeddef __rsub__(self, other):"""右减法"""return Fraction(other) - selfdef __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 NotImplementeddef __rmul__(self, other):"""右乘法"""return self * otherdef __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 NotImplementeddef __rtruediv__(self, other):"""右除法"""return Fraction(other) / selfdef __floordiv__(self, other):"""整除 //"""result = self / otherreturn Fraction(int(result.to_float()), 1)def __mod__(self, other):"""取模 %"""if isinstance(other, Fraction):quotient = self // otherreturn self - quotient * otherreturn NotImplementeddef __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 算术运算的数学原理

分数运算遵循标准的数学规则:

  • 加法ab+cd=ad+bcbd\frac{a}{b} + \frac{c}{d} = \frac{ad + bc}{bd}ba+dc=bdad+bc
  • 减法ab−cd=ad−bcbd\frac{a}{b} - \frac{c}{d} = \frac{ad - bc}{bd}badc=bdadbc
  • 乘法ab×cd=acbd\frac{a}{b} \times \frac{c}{d} = \frac{ac}{bd}ba×dc=bdac
  • 除法ab÷cd=ab×dc=adbc\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.valuedef __bool__(self):"""转换为布尔值"""return self.value != 0def __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] = valuedef __delitem__(self, index):"""支持删除元素"""del self._data[index]def __len__(self):"""返回列表长度"""return len(self._data)def __contains__(self, item):"""支持in操作符"""return item in self._datadef __iter__(self):"""支持迭代"""return iter(self._data)def append(self, item):"""添加元素"""self._data.append(item)# 统计方法def mean(self):"""计算平均值"""if not self._data:return 0return sum(self._data) / len(self._data)def median(self):"""计算中位数"""if not self._data:return 0sorted_data = sorted(self._data)n = len(sorted_data)mid = n // 2if n % 2 == 0:return (sorted_data[mid-1] + sorted_data[mid]) / 2else:return sorted_data[mid]def mode(self):"""计算众数"""if not self._data:return []from collections import Countercounter = 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 0mean_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.5def __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 Realclass 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)@propertydef dim(self):"""返回向量维度"""return self._dim@propertydef components(self):"""返回向量分量(只读)"""return self._components@propertydef magnitude(self):"""计算向量模长"""return math.sqrt(sum(x**2 for x in self._components))@propertydef unit_vector(self):"""返回单位向量"""mag = self.magnitudeif 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 Falsereturn (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 NotImplementeddef __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 NotImplementeddef __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 NotImplementeddef __rmul__(self, other):"""右数乘"""if isinstance(other, Real):return self * otherreturn NotImplementeddef __truediv__(self, other):"""向量数除"""if isinstance(other, Real):if other == 0:raise ZeroDivisionError("不能除以零")return Vector(*(x / other for x in self._components))return NotImplementeddef __matmul__(self, other):"""向量叉积(使用@运算符)"""if isinstance(other, Vector):if self._dim != 3 or other._dim != 3:raise ValueError("叉积只支持三维向量")x1, y1, z1 = self._componentsx2, y2, z2 = other._componentsreturn Vector(y1 * z2 - z1 * y2,z1 * x2 - x1 * z2,x1 * y2 - y1 * x2)return NotImplementeddef dot(self, other):"""点积(方法形式)"""return self * otherdef cross(self, other):"""叉积(方法形式)"""return self @ otherdef 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)) < tolerancedef 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._dimdef __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()

代码自查与说明

  1. 数学正确性

    • 向量运算遵循数学定义
    • 处理了边界情况(如零向量)
    • 使用适当的数值稳定性技巧
  2. 代码规范

    • 完整的文档字符串
    • 清晰的变量命名
    • 适当的错误处理
  3. 魔法方法覆盖

    • __init__: 对象初始化
    • __str__/__repr__: 字符串表示
    • 比较运算方法
    • 算术运算方法
    • 容器协议方法
  4. 功能完整性

    • 支持2D和3D向量
    • 实现所有基本向量运算
    • 提供高级数学功能

这个向量类展示了如何综合运用各种魔法方法来创建一个功能完整、接口自然的数学类。

9. 总结

通过本文的学习,我们深入了解了Python魔法方法的核心概念:

9.1 主要收获

  1. 魔法方法的作用:让自定义类具有内置类型一样的行为

  2. 常用魔法方法分类

    • 构造初始化:__init__, __new__, __del__
    • 对象表示:__str__, __repr__
    • 比较运算:__eq__, __lt__, 等
    • 算术运算:__add__, __sub__, __mul__, 等
    • 类型转换:__int__, __float__, __bool__
    • 容器协议:__getitem__, __setitem__, __len__
  3. 最佳实践

    • 始终返回NotImplemented而不是抛出TypeError
    • 实现反向运算方法(如__radd__
    • 区分__str____repr__的用途
    • 保持数学运算的准确性

9.2 进一步学习

要深入学习魔法方法,建议:

  1. 阅读Python官方文档的数据模型章节
  2. 研究标准库中类的实现(如collections模块)
  3. 实践实现更复杂的魔法方法,如上下文管理器(__enter__, __exit__

掌握魔法方法是成为Python高级程序员的重要一步,它们让代码更加Pythonic和优雅。

http://www.dtcms.com/a/415966.html

相关文章:

  • 数字化转型:概念性名词浅谈(第五十四讲)
  • 用自然语言提问的艺术:高效学习的核心技能
  • 推广型网站制作哪家好网站访问量怎么做
  • wap网站开发 费用深圳seo招聘
  • 深度学习——加载数据
  • 网站不兼容怎么办百度竞价运营
  • 做网站的合作案例影响网站打开速度的因素
  • 网站备案一般要多久网站备案是备什么
  • 自己怎么做网站卖东西建设网站挂广告赚钱
  • 加强网站微信信息编辑队伍建设查询建设工程规范的网站
  • 最炫表白网站html5源码重庆大足网站制作公司哪家专业
  • 网站备案怎么关闭网站wordpress ssl 500
  • 网站建设济南有做的吗政务公开和网站建设工作的建议
  • 【BOOST升压电路】2022-12-8
  • Linux学习笔记(七)--进程状态
  • 网站内搜索关键字怎么做手机app软件
  • 招标网站有哪些网站后台密码忘记了怎么办
  • 第52篇:AI+交通:智能驾驶、交通流优化与智慧物流
  • SQL 优化实战案例:从慢查询到高性能的完整指南
  • 响应式网站做优化好吗wordpress的d8主题
  • MATLAB基于加速遗传算法投影寻踪模型的企业可持续发展能力评价研究
  • 个人网站怎么做收款链接网络营销案例分析报告
  • 做物流网站模块科汛kesioncms网站系统
  • Kafka 合格候选主副本(ELR)在严格 min ISR 约束下提升选主韧性
  • 成都市城乡建设局网站法制教育网站
  • PyQt和PySide中使用Qt Designer
  • 网站建设虚拟云虚拟主机怎么做2个网站
  • 网站建设合同附加协议江门专业做网站
  • 郑州网站制丹东静态管理
  • 网站建设落后发言网站收录作用