python 魔术方法详解

python 魔术方法详解

python 魔术方法详解

在这里插入图片描述

一、魔术方法详解

魔术方法(Magic Methods),也称为特殊方法(Special Methods)或双下方法(Dunder Methods),是 Python 中以双下划线开头和结尾的方法(例如 __init__, __str__)。它们不是让你直接调用的,而是由 Python 解释器在特定场景下自动调用的。它们为自定义类提供了与 Python 内置类型和操作符交互的能力,让你可以定义对象在各种操作下的行为。

1、核心概念

  1. 自动调用:你定义它们,但 Python 在特定事件发生时(如对象初始化、加法运算、打印对象等)自动调用它们。
  2. 增强功能:它们赋予你的类特殊的行为,使其可以像内置类型一样工作。
  3. 约定俗成:每个魔术方法都有特定的名称和预期的行为。遵循这些约定很重要。

2、常见的魔术方法分类及详解

2.1、构造与销毁 (__init__, __new__, __del__)

  • __del__(self):析构器方法。当对象的引用计数变为零(或垃圾回收器准备回收对象)时被调用。谨慎使用!因为 Python 的内存管理(引用计数和垃圾回收)使得很难精确预测 __del__ 何时被调用。通常不需要手动实现,更好的资源清理方式是使用上下文管理器 (with 语句)。

__new__(cls, [...]):在 __init__之前调用。它负责创建返回类的一个新实例。它接收类本身(cls)作为第一个参数。通常不需要覆盖,但在需要控制实例创建过程(如单例模式、不可变对象、子类化不可变类型)时很有用。

classSingleton: _instance =Nonedef__new__(cls):if cls._instance isNone: cls._instance =super().__new__(cls)return cls._instance s1 = Singleton() s2 = Singleton()print(s1 is s2)# 输出: True

__init__(self, [...]):最常用的魔术方法。在对象实例创建之后被调用。用于初始化新实例的属性(设置初始状态)。它接受传递给类构造函数的参数(通常是 __init__(self, param1, param2, ...))。

classPoint:def__init__(self, x, y): self.x = x self.y = y p = Point(3,4)# 调用 Point.__init__(p, 3, 4)

2.2、字符串表示 (__str__, __repr__)

  • __str__(self):当调用 str(object)print(object)format(object) 时调用。目的是返回一个对用户友好、可读性高的字符串表示,描述对象的内容。

__repr__(self):当调用 repr(object) 或在交互式解释器中直接输入对象名称时调用。目的是返回一个对开发者友好、明确的字符串表示,通常是一个可以用来重新创建该对象的有效 Python 表达式(如果可能的话)。如果 __str__ 未定义,__repr__ 会作为其默认回退。

classPoint:def__init__(self, x, y): self.x = x self.y = y def__str__(self):returnf"Point({self.x}, {self.y})"def__repr__(self):returnf"Point(x={self.x}, y={self.y})" p = Point(3,4)print(p)# 输出: Point(3, 4) (调用 __str__)print(repr(p))# 输出: Point(x=3, y=4) (调用 __repr__)

2.3、比较操作符 (__eq__, __ne__, __lt__, __le__, __gt__, __ge__)

  • 这些方法定义了对象在使用 ==, !=, <, <=, >, >= 操作符时的行为。
  • __eq__(self, other):定义 self == other 的行为。
  • __ne__(self, other):定义 self != other 的行为。默认实现通常是 not self == other
  • __lt__(self, other):定义 self < other 的行为。
  • __le__(self, other):定义 self <= other 的行为。
  • __gt__(self, other):定义 self > other 的行为。
  • __ge__(self, other):定义 self >= other 的行为。

如果只实现了其中几个(如 __lt__),Python 可能尝试用它们推导出其他比较操作(但这并不总是可靠)。使用 functools.total_ordering 装饰器可以自动补全缺失的比较方法,只需定义 __eq__ 和另一个(如 __lt__)。

classPoint:def__init__(self, x, y): self.x = x self.y = y def__eq__(self, other):return self.x == other.x and self.y == other.y def__lt__(self, other):# 定义小于,通常用于排序return(self.x **2+ self.y **2)<(other.x **2+ other.y **2) p1 = Point(1,2) p2 = Point(1,2) p3 = Point(3,4)print(p1 == p2)# Trueprint(p1 != p3)# True (默认使用 not __eq__)print(p1 < p3)# True

2.4、算术运算符重载 (__add__, __sub__, __mul__, __truediv__, __floordiv__, __mod__, __pow__, … 及其 __r*____i*__ 变体)

  • 这些方法定义了对象在使用 +, -, *, /, //, %, ** 等操作符时的行为。
  • __add__(self, other):定义 self + other 的行为。
  • __sub__(self, other):定义 self - other 的行为。
  • … 其他类似。
  • __radd__(self, other), __rsub__(self, other), …:定义 other + self, other - self 的行为(当 self 是操作符右侧的操作数时调用)。这用于处理 self 不支持与 other 类型直接运算的情况。

__iadd__(self, other), __isub__(self, other), …:定义 self += other, self -= other原地操作的行为。如果未实现,Python 会尝试使用 __add__ / __sub__ 等,并将结果赋值给 self

classVector: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__iadd__(self, other):# 原地加法 += self.x += other.x self.y += other.y return self # 通常返回 selfdef__repr__(self):returnf"Vector({self.x}, {self.y})" v1 = Vector(1,2) v2 = Vector(3,4) v3 = v1 + v2 # 调用 v1.__add__(v2) -> Vector(4, 6) v1 += v2 # 调用 v1.__iadd__(v2) -> v1 变为 Vector(4, 6)

2.5、类型转换 (__int__, __float__, __bool__, __complex__)

  • 这些方法定义了对象在被传递给内置函数 int(), float(), bool(), complex() 时的行为。

__bool__(self):特别重要,定义了对象在布尔上下文中(如 if obj:)的真假值。如果未定义 __bool__,Python 会尝试调用 __len__()(如果定义了),非零长度则为真,否则为假。如果两者都未定义,则所有实例都被视为真。

classTruthy:def__bool__(self):returnTrueclassFalsy:def__bool__(self):returnFalseclassNoBoolButLen:def__len__(self):return0if Truthy():print("True")# 输出 Trueif Falsy():print("This won't")# 不输出if NoBoolButLen():print("This won't either")# 不输出 (__len__ 返回 0)

2.6、容器/序列模拟 (__len__, __getitem__, __setitem__, __delitem__, __contains__, __iter__, __reversed__, __next__)

  • 这些方法让自定义类表现得像列表、字典、集合等容器。
  • __len__(self):定义 len(obj) 的行为,返回容器中元素的个数。
  • __getitem__(self, key):定义 obj[key] 的行为,实现索引或键访问。
  • __setitem__(self, key, value):定义 obj[key] = value 的行为,实现索引或键赋值。
  • __delitem__(self, key):定义 del obj[key] 的行为,实现索引或键删除。
  • __contains__(self, item):定义 item in obj 的行为,检查元素是否存在。
  • __iter__(self):定义 iter(obj)for item in obj 的行为。应该返回一个实现了 __next__ 的迭代器对象(通常是 self 如果它自己实现了 __next__)。
  • __next__(self):定义迭代器对象在 next(iterator)for 循环中的下一个值的行为。当没有更多元素时,必须引发 StopIteration 异常。

__reversed__(self):定义 reversed(obj) 的行为,返回一个反向迭代器。

classMyList:def__init__(self, data): self.data =list(data)def__len__(self):returnlen(self.data)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__contains__(self, item):return item in self.data def__iter__(self): self.current =0return self def__next__(self):if self.current <len(self.data): item = self.data[self.current] self.current +=1return item else:raise StopIteration my_list = MyList([1,2,3])print(len(my_list))# 3print(my_list[1])# 2 my_list[1]=20print(2in my_list)# Falseprint(20in my_list)# Truefor item in my_list:# 1, 20, 3print(item)

2.7、可调用对象 (__call__)

__call__(self, [...]):定义对象本身像函数一样被调用时的行为 obj(...)。这使得实例可以像函数一样使用。

classAdder:def__init__(self, base): self.base = base def__call__(self, x):return self.base + x add5 = Adder(5) result = add5(3)# 调用 add5.__call__(3)print(result)# 8

2.8、上下文管理器 (__enter__, __exit__)

  • 这些方法使对象可以用在 with 语句中,用于资源管理(如自动关闭文件)。
  • __enter__(self):在进入 with 块时调用,其返回值会赋值给 as 后的变量(如果有)。

__exit__(self, exc_type, exc_value, traceback):在退出 with 块时调用(无论正常退出还是异常退出)。它接收异常信息参数。如果返回 True 或真值,则表示异常已被处理,不会向外传播;返回 None 或假值,则异常会向外传播。

classManagedFile:def__init__(self, filename, mode): self.filename = filename self.mode = mode self.file=Nonedef__enter__(self): self.file=open(self.filename, self.mode)return self.filedef__exit__(self, exc_type, exc_value, traceback):if self.file: self.file.close()# 可以在这里处理异常,返回 True 表示已处理returnFalse# 让异常正常传播with ManagedFile('example.txt','w')as f: f.write('Hello, world!')# 文件在这里自动关闭

2.9、 属性访问控制 (__getattr__, __getattribute__, __setattr__, __delattr__)

  • __getattr__(self, name):当试图访问一个不存在的属性时调用。name 是属性名。可用于实现动态属性或惰性加载。
  • __getattribute__(self, name):当试图访问任何属性(无论是否存在)时调用。谨慎使用!很容易导致无限递归(访问 self.name 会再次触发 __getattribute__)。通常使用 super().__getattribute__(name) 来避免递归。覆盖它会显著影响性能。
  • __setattr__(self, name, value):当试图给任何属性赋值时调用。同样要注意避免递归(赋值 self.name = value 会再次触发 __setattr__)。通常使用 super().__setattr__(name, value) 或直接操作 __dict__

__delattr__(self, name):当试图删除一个属性时调用。

classLazy:def__init__(self): self._expensive_data =Nonedef__getattr__(self, name):if name =='expensive_data':if self._expensive_data isNone:print("Calculating expensive data...") self._expensive_data ="Computed Value"# 模拟计算return self._expensive_data raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") lazy_obj = Lazy()print(lazy_obj.expensive_data)# 第一次访问会计算print(lazy_obj.expensive_data)# 第二次访问直接返回缓存

2.10、其他重要方法

  • __hash__(self):定义 hash(obj) 的行为,返回一个整数哈希值。如果定义了 __eq__,通常也应该定义 __hash__(除非对象是可变的)。哈希值应满足:相等的对象必须有相等的哈希值(但不要求不等的对象哈希值不同)。不可变对象通常需要实现。
  • __dir__(self):定义 dir(obj) 的行为,返回一个包含对象属性名称的列表。

3、总结

魔术方法是 Python 面向对象编程的核心机制之一。它们允许你:

  • 定制初始化 (__init__)
  • 定义对象如何被表示 (__str__, __repr__)
  • 控制比较行为 (__eq__, __lt__, …)
  • 重载运算符 (__add__, __sub__, …)
  • 模拟内置容器 (__len__, __getitem__, …)
  • 使对象可调用 (__call__)
  • 支持 with 语句 (__enter__, __exit__)
  • 管理属性访问 (__getattr__, __setattr__, …)
  • 提供类型转换 (__int__, __float__, __bool__)
  • 定义哈希值 (__hash__)

二、代码示例

import math classMatrix:def__init__(self, rows, cols=None, data=None):"""初始化矩阵 参数: rows: 行数或二维列表 cols: 列数(当rows为整数时) data: 初始数据(可选) """ifisinstance(rows,list):# 从二维列表创建 self.data = rows self.rows =len(rows) self.cols =len(rows[0])if self.rows >0else0else:# 指定行列创建 self.rows = rows self.cols = cols if cols isnotNoneelse rows self.data =[[0]*self.cols for _ inrange(self.rows)]if data:for i inrange(min(self.rows,len(data))):for j inrange(min(self.cols,len(data[i]))): self.data[i][j]= data[i][j]def__str__(self):"""美化输出矩阵""" max_len =max(len(str(x))for row in self.data for x in row)return"[\n"+"\n".join(" ["+", ".join(f"{x:>{max_len}}"for x in row)+"]"for row in self.data )+"\n]"def__repr__(self):"""返回可执行的表示形式"""returnf"Matrix({self.data})"def__add__(self, other):"""矩阵加法"""if self.rows != other.rows or self.cols != other.cols:raise ValueError("矩阵维度不匹配")return Matrix([[self.data[i][j]+ other.data[i][j]for j inrange(self.cols)]for i inrange(self.rows)])def__sub__(self, other):"""矩阵减法"""if self.rows != other.rows or self.cols != other.cols:raise ValueError("矩阵维度不匹配")return Matrix([[self.data[i][j]- other.data[i][j]for j inrange(self.cols)]for i inrange(self.rows)])def__mul__(self, other):"""矩阵乘法(支持标量和矩阵)"""ifisinstance(other,(int,float)):# 标量乘法return Matrix([[x * other for x in row]for row in self.data ])elifisinstance(other, Matrix):# 矩阵乘法if self.cols != other.rows:raise ValueError("矩阵维度不匹配") result = Matrix(self.rows, other.cols)for i inrange(self.rows):for j inrange(other.cols): result[i, j]=sum( self[i, k]* other[k, j]for k inrange(self.cols))return result else:raise TypeError("不支持的乘法类型")def__rmul__(self, other):"""右乘标量"""ifisinstance(other,(int,float)):return self * other raise TypeError("不支持的乘法类型")def__matmul__(self, other):"""矩阵乘法运算符 @ (Python 3.5+)"""return self * other def__getitem__(self, index):"""索引访问"""ifisinstance(index,tuple)andlen(index)==2: i, j = index return self.data[i][j]elifisinstance(index,int):return self.data[index]raise IndexError("无效索引")def__setitem__(self, index, value):"""索引赋值"""ifisinstance(index,tuple)andlen(index)==2: i, j = index self.data[i][j]= value elifisinstance(index,int): self.data[index]= value else:raise IndexError("无效索引")def__eq__(self, other):"""矩阵相等比较"""return( self.rows == other.rows and self.cols == other.cols andall( self.data[i][j]== other.data[i][j]for i inrange(self.rows)for j inrange(self.cols)))def__abs__(self):"""计算行列式"""if self.rows != self.cols:raise ValueError("只有方阵才有行列式")return self._determinant(self.data)def_determinant(self, matrix):"""递归计算行列式""" n =len(matrix)if n ==1:return matrix[0][0]if n ==2:return matrix[0][0]*matrix[1][1]- matrix[0][1]*matrix[1][0] det =0for j inrange(n): minor =[[matrix[i][k]for k inrange(n)if k != j]for i inrange(1, n)] sign =(-1)** j det += sign * matrix[0][j]* self._determinant(minor)return det def__invert__(self):"""矩阵转置"""return Matrix([[self.data[j][i]for j inrange(self.rows)]for i inrange(self.cols)])def__call__(self, vector):"""将矩阵作为线性变换函数"""ifnotisinstance(vector,list)orlen(vector)!= self.cols:raise ValueError("输入必须是长度为矩阵列数的列表")return[sum(self[i, j]* vector[j]for j inrange(self.cols))for i inrange(self.rows)]# 示例用法if __name__ =="__main__":# 创建矩阵 A = Matrix([[1,2,3],[4,5,6],[7,8,9]]) B = Matrix([[9,8,7],[6,5,4],[3,2,1]])print("矩阵 A:")print(A)print("\n矩阵 B:")print(B)print("\n矩阵加法 A + B:")print(A + B)print("\n矩阵乘法 A * B:")print(A * B)print("\n使用 @ 运算符的矩阵乘法 A @ B:")print(A @ B)print("\n标量乘法 2 * A:")print(2* A)print("\n矩阵转置 ~A:")print(~A)print("\n矩阵行列式 |A| =",abs(A))print("\n矩阵作为线性变换 A([1, 2, 3]) =", A([1,2,3]))print("\n矩阵相等比较 A == B:", A == B)

运行结果:

矩阵 A:[[1,2,3][4,5,6][7,8,9]] 矩阵 B:[[9,8,7][6,5,4][3,2,1]] 矩阵加法 A + B:[[10,10,10][10,10,10][10,10,10]] 矩阵乘法 A * B:[[30,24,18][84,69,54][138,114,90]] 使用 @ 运算符的矩阵乘法 A @ B:[[30,24,18][84,69,54][138,114,90]] 标量乘法 2* A:[[2,4,6][8,10,12][14,16,18]] 矩阵转置 ~A:[[1,4,7][2,5,8][3,6,9]] 矩阵行列式 |A|=0 矩阵作为线性变换 A([1,2,3])=[14,32,50] 矩阵相等比较 A == B: False PS C:\Users\徐鹏\Desktop\新建文件夹 (2)>
在这里插入图片描述
在这里插入图片描述

Read more

B树与B+树:从原理到实现的深度解析

第1章:引言:为什么需要多路平衡查找树? 1.1 计算机存储层次结构带来的挑战 在现代计算机系统中,存储层次结构从寄存器、高速缓存、主内存到磁盘,访问速度差异巨大。以典型的现代计算机为例: * CPU寄存器:访问延迟约0.3纳秒 * L1高速缓存:访问延迟约0.9纳秒 * L2高速缓存:访问延迟约2.8纳秒 * 主内存:访问延迟约12纳秒 * 固态硬盘:访问延迟约25微秒(25,000纳秒) * 机械硬盘:访问延迟约10毫秒(10,000,000纳秒) 这种访问延迟的指数级差异催生了局部性原理的优化思想。然而,当数据规模达到千万甚至亿级时,传统的二叉树结构面临严重挑战: text 二叉查找树的最坏情况: [1] \ [2] \ [3] \ ... \ [1000000] 1.2 二叉树在外部存储中的局限性 考虑一个包含1亿条记录的数据库,假设每个记录需要100字节存储空间。如果使用二叉查找树: * 平均树高约为log₂(100,

By Ne0inhk
【算法通关指南:数据结构与算法篇 】二叉树相关算法题:1.新二叉树 2.二叉树的遍历

【算法通关指南:数据结构与算法篇 】二叉树相关算法题:1.新二叉树 2.二叉树的遍历

🔥小龙报:个人主页 🎬作者简介:C++研发,嵌入式,机器人方向学习者 ❄️个人专栏:《算法通关指南》 ✨ 永远相信美好的事情即将发生 文章目录 * 前言 * 二、新二叉树 * 2.1题目 * 2.2 算法原理 * 2.3代码 * 三、 二叉树的遍历 * 3.1题目 * 3.2 算法原理 * 3.3代码 * 总结与每日励志 前言 本专栏聚焦算法题实战,系统讲解算法模块:以《c++编程》,《数据结构和算法》《基础算法》《算法实战》 等几个板块以题带点,讲解思路与代码实现,帮助大家快速提升代码能力ps:本章节题目分两部分,比较基础笔者只附上代码供大家参考,其他的笔者会附上自己的思考和讲解,希望和大家一起努力见证自己的算法成长 二、新二叉树 2.

By Ne0inhk
【强化学习】区分理解: 时序差分(TD)、蒙特卡洛(MC)、动态规划(DP)

【强化学习】区分理解: 时序差分(TD)、蒙特卡洛(MC)、动态规划(DP)

📢本篇文章是博主强化学习(RL)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在👉强化学习专栏:        【强化学习】- 【强化学习进阶】(3)---《 区分理解: 时序差分(TD)、蒙特卡洛(MC)、动态规划(DP)》 区分理解: 时序差分(TD)、蒙特卡洛(MC)、动态规划(DP) 目录 一、前言 二、时序差分(Temporal-Difference,TD) 1. 背景 2. TD方法的核心思想 3. TD与其他方法的对比 4. 常见的TD算法 三、 蒙特卡洛(Monte Carlo, MC)

By Ne0inhk
Flutter 三方库 matrix 鸿蒙终端底层复杂超维数学算力适配突破:无缝植入极限级张量系统与密集线性代数矩阵运算推演算法,解锁端侧图形处理边界-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 matrix 鸿蒙终端底层复杂超维数学算力适配突破:无缝植入极限级张量系统与密集线性代数矩阵运算推演算法,解锁端侧图形处理边界-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 matrix 鸿蒙终端底层复杂超维数学算力适配突破:无缝植入极限级张量系统与密集线性代数矩阵运算推演算法,全面解锁端侧图形视觉处理边界并拔高数据分析算力上限 在图形学渲染、物理引擎模拟、复杂地理坐标转换以及端侧小型机器学习框架中,底层的矩阵运算(Matrix Operations)是决速步骤。matrix 库是一个专注于高性能线性代数计算的 Dart 库。本文将详解该库在 OpenHarmony 环境下的适配与实战应用。 封面 前言 什么是 matrix?它为 Dart 提供了一套类似于 NumPy 的多维数组运算接口。在鸿蒙操作系统这种强调极致流畅度和复杂视觉动效的系统中,利用高效的矩阵算法可以显著提升自定义 Canvas 绘图或实时传器数据处理的性能,避免因 Dart 层的低效循环导致的 UI 掉帧。 一、原理解析 1.1 基础概念 matrix 库核心基于

By Ne0inhk