当前位置:首页>python>Python 魔法方法:从理解到掌握

Python 魔法方法:从理解到掌握

  • 2026-02-07 16:43:53
Python 魔法方法:从理解到掌握

目录

  • 1 什么是魔法方法
  • 2 核心魔法方法详解
  • 3 何时使用魔法方法
  • 4 实用案例分析
    • 4.1 案例一:实现一个简单的分数类
    • 4.2 案例二:实现一个智能配置文件类
  • 5 常见问题
  • 6 魔法方法分类速查表

1. 什么是魔法方法

1.1 魔法方法概念

魔法方法(Magic Methods),又称为双下划线方法特殊方法,是 Python 中一种特殊的命名约定。这些方法名以双下划线开头和结尾,例如 __init____str__ 等。

核心特性

  1. 自动调用:Python 解释器在特定情况下自动调用这些方法
  2. 约定优于配置:通过实现特定方法,让对象支持特定操作
  3. 语法糖背后的秘密:简化语法背后的实际实现
  4. Pythonic 的关键:让自定义对象拥有内置类型一样的行为

想象你要开一家餐厅,你需要:

  • __init__):准备食材,初始化厨房
  • __str__):给顾客看的介绍
  • __repr__):给厨师看的详细配方
  • __call__):让餐厅可以被"调用"
  • __enter__/__exit__):管理营业时间,确保关门后打扫干净

基础示例

classSimpleClass:def__init__(self, value):        self.value = value        print("对象已初始化")def__str__(self):returnf"SimpleClass(value={self.value})"def__repr__(self):returnf"SimpleClass({self.value!r})"

1.2 魔法方法的意义

没有魔法方法的局限性

classPerson:def__init__(self, name, age):        self.name = name        self.age = agep = Person("Alice"25)print(p)  # 输出:<__main__.Person object at 0x...> - 不友好!p2 = Person("Alice"25)print(p == p2)  # False - 比较内存地址,不是内容!# len(p)  # 报错:对象不支持len()操作

有了魔法方法的世界

classPerson:def__init__(self, name, age):        self.name = name        self.age = agedef__str__(self):"""给用户看的友好表示"""returnf"{self.name}{self.age}岁"def__repr__(self):"""给开发者看的精确表示"""returnf"Person(name={self.name!r}, age={self.age})"def__eq__(self, other):"""内容相等比较"""ifnot isinstance(other, Person):returnFalsereturn self.name == other.name and self.age == other.agedef__len__(self):"""假设长度是名字长度+年龄"""return len(self.name) + self.agep = Person("Alice"25)print(p)  # 输出:Alice, 25岁print(repr(p))  # 输出:Person(name='Alice', age=25)print(p == Person("Alice"25))  # Trueprint(len(p))  # 30 (5 + 25)

提示

  • 魔法方法是 Python 中双下划线开头结尾的特殊方法
  • 通过实现魔法方法,可以让自定义对象支持内置操作
  • 它们是 Python "协议"的具体实现,让代码更 Pythonic

2. 核心魔法方法介绍

2.1 对象生命周期方法

__new__ 方法

作用:创建对象实例(真正的构造函数)调用时机:在 __init__ 之前,创建对象时典型用途:单例模式、不可变对象、对象池、元类编程

classSingleton:"""单例模式实现"""    _instance = Nonedef__new__(cls, *args, **kwargs):        print("__new__ 被调用,创建实例")if cls._instance isNone:# 首次调用,创建新实例            cls._instance = super().__new__(cls)# 后续调用直接返回已有实例return cls._instancedef__init__(self, name):        print("__init__ 被调用,初始化实例")# 注意:单例模式下__init__会被多次调用!        self.name = name# 测试s1 = Singleton("第一")s2 = Singleton("第二")print(f"s1 is s2: {s1 is s2}")  # Trueprint(f"s1.name: {s1.name}")    # "第二"(注意:被覆盖了!)

提示

  • __new__:创建对象,必须返回实例
  • __init__:初始化对象,不能返回值
  • __del__:清理对象,非确定性调用

__init__ 方法

作用:初始化对象实例调用时机:对象创建后立即调用最佳用法:参数验证、属性初始化、计算派生属性

classStudent:def__init__(self, name, score):# 参数验证ifnot isinstance(name, str):raise TypeError("姓名必须是字符串")ifnot0 <= score <= 100:raise ValueError("分数必须在0-100之间")        self.name = name        self.score = score# 计算派生属性        self.grade = self._calculate_grade()        self.passed = score >= 60def_calculate_grade(self):"""根据分数计算等级"""if self.score >= 90:return"A"elif self.score >= 80:return"B"elif self.score >= 70:return"C"elif self.score >= 60:return"D"else:return"F"def__repr__(self):returnf"Student({self.name!r}{self.score})"# 使用try:    stu = Student("张三"85)    print(f"{stu.name}: 成绩{stu.grade}{'及格'if stu.passed else'不及格'}")# 张三: 成绩B, 及格except ValueError as e:    print(f"创建失败: {e}")

__del__ 方法

注意:Python 的垃圾回收机制可能不会立即调用 __del__

classResourceHolder:def__init__(self, name):        self.name = name        print(f"资源 {self.name} 已创建")def__del__(self):"""清理资源,但不要依赖它!"""        print(f"资源 {self.name} 正在清理...")# 这里的打印可能永远不会执行!defclose(self):"""显式关闭方法,更可靠"""        print(f"资源 {self.name} 已显式关闭")# 更好的替代:上下文管理器classSafeResource:def__init__(self, name):        self.name = namedef__enter__(self):        print(f"打开资源: {self.name}")return selfdef__exit__(self, exc_type, exc_val, exc_tb):        print(f"关闭资源: {self.name}")if exc_type:            print(f"发生异常: {exc_val}")returnFalse# 不抑制异常

2.2 字符串表示方法

__str__ vs __repr__ 详细对比

特性
__str____repr__
目标用户
最终用户
开发者
主要目的
可读的、友好的展示
明确的、无歧义的表示
调用方式str(obj)
print(obj)
repr(obj)
, 交互式环境
实现原则
易读性优先
应能用于重建对象
回退机制
若无 __str__,使用 __repr__
必须实现
classBook:def__init__(self, title, author, year):        self.title = title        self.author = author        self.year = yeardef__str__(self):"""给用户看的:简洁友好的描述"""returnf"《{self.title}》 - {self.author} ({self.year})"def__repr__(self):"""给开发者看的:详细准确的表示"""# 注意:使用!r确保字符串被正确引用returnf"Book(title={self.title!r}, author={self.author!r}, year={self.year})"def__format__(self, format_spec):"""支持format()函数和f-string"""if format_spec == "short":returnf"{self.title[:10]}..."elif format_spec == "bib":returnf"{self.author} ({self.year}). {self.title}."else:return str(self)book = Book("Python编程从入门到实践""Eric Matthes"2016)print(str(book))      # 《Python编程从入门到实践》 - Eric Matthes (2016)print(repr(book))     # Book(title='Python编程从入门到实践', author='Eric Matthes', year=2016)print(f"{book:short}"# Python编程... print(f"{book:bib}")  # Eric Matthes (2016). Python编程从入门到实践.# 在交互式环境中:# >>> book# Book(title='Python编程从入门到实践', author='Eric Matthes', year=2016)

提示

  1. 总是实现 __repr__,至少用于调试
  2. __str__ 是可选的,但建议提供用户友好版本
  3. repr(obj) 应该产生一个字符串,使得 eval(repr(obj)) == obj 成立(安全性允许的情况下)

2.3 比较运算符方法

为什么需要自定义比较?

默认情况下,Python 使用对象的内存地址进行比较:

classPoint:def__init__(self, x, y):        self.x = x        self.y = yp1 = Point(12)p2 = Point(12)print(p1 == p2)  # False - 比较内存地址!print(p1 is p2)  # False - 确实是不同对象

完整比较方法实现

import functools@functools.total_ordering  # 装饰器自动补全其他比较方法classPoint:def__init__(self, x, y):        self.x = x        self.y = ydef__eq__(self, other):"""等于判断"""ifnot isinstance(other, Point):returnNotImplemented# 让Python尝试其他比较方式return self.x == other.x and self.y == other.ydef__lt__(self, other):"""小于判断(定义排序规则)"""ifnot isinstance(other, Point):returnNotImplemented# 先比较x,再比较yreturn (self.x, self.y) < (other.x, other.y)def__repr__(self):returnf"Point({self.x}{self.y})"# 使用装饰器后,自动获得所有比较操作p1 = Point(12)p2 = Point(13)p3 = Point(12)print(p1 == p3)  # Trueprint(p1 != p2)  # Trueprint(p1 < p2)   # Trueprint(p1 <= p3)  # Trueprint(p2 > p1)   # Trueprint(p2 >= p1)  # True# 排序points = [Point(31), Point(15), Point(12), Point(20)]points.sort()print(points)  # [Point(1, 2), Point(1, 5), Point(2, 0), Point(3, 1)]

__hash__ 方法

提示

  1. 相等的对象必须有相同的哈希值
  2. 哈希值在对象生命周期内应保持不变
  3. 如果定义了 __eq__,通常也需要定义 __hash__
classImmutablePoint:"""不可变点,可用作字典键"""    __slots__ = ('_x''_y')  # 禁止动态添加属性def__init__(self, x, y):        self._x = x        self._y = y    @propertydefx(self):return self._x    @propertydefy(self):return self._ydef__eq__(self, other):ifnot isinstance(other, ImmutablePoint):returnFalsereturn self._x == other._x and self._y == other._ydef__hash__(self):# 使用元组计算哈希值return hash((self._x, self._y))def__repr__(self):returnf"ImmutablePoint({self._x}{self._y})"# 可以作为字典键和集合元素p1 = ImmutablePoint(12)p2 = ImmutablePoint(12)p3 = ImmutablePoint(34)# 字典使用cache = {p1: "点A", p3: "点B"}print(cache[p1])      # "点A"print(cache.get(p2))  # "点A" - p1和p2相等# 集合使用point_set = {p1, p2, p3}print(len(point_set))  # 2(p1和p2相同)

2.4 数值运算方法

基本算术运算示例

classVector:"""二维向量类"""def__init__(self, x, y):        self.x = x        self.y = y# 加法def__add__(self, other):"""向量加法"""ifnot isinstance(other, Vector):returnNotImplementedreturn Vector(self.x + other.x, self.y + other.y)# 减法def__sub__(self, other):ifnot isinstance(other, Vector):returnNotImplementedreturn Vector(self.x - other.x, self.y - other.y)# 标量乘法def__mul__(self, scalar):"""向量 * 标量"""ifnot isinstance(scalar, (int, float)):returnNotImplementedreturn Vector(self.x * scalar, self.y * scalar)# 反向乘法(标量 * 向量)def__rmul__(self, scalar):return self.__mul__(scalar)# 点积def__matmul__(self, other):"""向量点积 @ 操作符 (Python 3.5+)"""ifnot isinstance(other, Vector):returnNotImplementedreturn self.x * other.x + self.y * other.y# 字符串表示def__str__(self):returnf"Vector({self.x}{self.y})"def__repr__(self):returnf"Vector({self.x}{self.y})"# 绝对值(向量长度)def__abs__(self):return (self.x ** 2 + self.y ** 2) ** 0.5# 使用v1 = Vector(12)v2 = Vector(34)print(v1 + v2)       # Vector(4, 6)print(v2 - v1)       # Vector(2, 2)print(v1 * 3)        # Vector(3, 6)print(2 * v2)        # Vector(6, 8)print(v1 @ v2)       # 11 (点积)print(abs(v1))       # 2.236 (向量长度)

完整数值运算方法参考表

方法
运算符
反向方法
说明
__add__+__radd__
加法
__sub__-__rsub__
减法
__mul__*__rmul__
乘法
__truediv__/__rtruediv__
真除法
__floordiv__//__rfloordiv__
地板除法
__mod__%__rmod__
取模
__pow__**__rpow__
幂运算
__matmul__@__rmatmul__
矩阵乘法
__and__&__rand__
按位与
__or__|__ror__
按位或
__xor__^__rxor__
按位异或
__lshift__<<__rlshift__
左移
__rshift__>>__rrshift__
右移
__neg__-
取负(一元)
__pos__+
取正(一元)
__abs__abs()
绝对值
__invert__~
按位取反
__round__round()
四舍五入
__floor__math.floor()
向下取整
__ceil__math.ceil()
向上取整
__trunc__math.trunc()
截断取整
__int__int()
转换为整数
__float__float()
转换为浮点数
__bool__bool()
转换为布尔值

2.5 容器协议方法

让自定义对象像内置容器一样工作

classPlaylist:"""音乐播放列表,支持多种容器操作"""def__init__(self, name):        self.name = name        self.songs = []        self.current_index = 0defadd_song(self, song):        self.songs.append(song)# 容器协议方法def__len__(self):"""支持 len() 函数"""return len(self.songs)def__getitem__(self, index):"""支持索引访问 playlist[index] 和切片 playlist[start:stop:step]"""if isinstance(index, slice):# 处理切片return self.songs[index]elif isinstance(index, int):# 处理整数索引,支持负数索引if index < 0:                index = len(self.songs) + indexif0 <= index < len(self.songs):return self.songs[index]raise IndexError("播放列表索引超出范围")else:raise TypeError("索引必须是整数或切片")def__setitem__(self, index, song):"""支持索引赋值 playlist[index] = song"""        self.songs[index] = songdef__delitem__(self, index):"""支持 del playlist[index]"""del self.songs[index]# 如果删除的是当前播放之前的歌曲,调整索引if index < self.current_index:            self.current_index -= 1def__contains__(self, song):"""支持 in 操作符"""return song in self.songsdef__iter__(self):"""支持迭代(for song in playlist)"""        self.current_index = 0# 重置迭代位置return selfdef__next__(self):"""迭代器协议"""if self.current_index < len(self.songs):            song = self.songs[self.current_index]            self.current_index += 1return songraise StopIterationdef__reversed__(self):"""支持 reversed() 函数"""return reversed(self.songs)defappend(self, song):"""模拟列表的 append 方法"""        self.songs.append(song)definsert(self, index, song):"""模拟列表的 insert 方法"""        self.songs.insert(index, song)if index <= self.current_index:            self.current_index += 1def__str__(self):returnf"播放列表《{self.name}》共有{len(self)}首歌"def__repr__(self):returnf"Playlist(name={self.name!r}, songs={self.songs})"# 使用示例playlist = Playlist("我的最爱")playlist.add_song("歌曲A")playlist.append("歌曲B")playlist.insert(1"歌曲C")print(len(playlist))           # 3print(playlist[0])             # "歌曲A"print(playlist[-1])            # "歌曲B"print(playlist[1:])            # ['歌曲C', '歌曲B']print("歌曲C"in playlist)     # True# 迭代for song in playlist:    print(f"播放: {song}")# 反向迭代for song in reversed(playlist):    print(f"倒序播放: {song}")# 修改playlist[0] = "新歌曲A"del playlist[1]print(list(playlist))          # ['新歌曲A', '歌曲B']

2.6 可调用对象协议

__call__ 方法:让对象像函数一样工作

classCounter:"""可调用计数器,保持状态"""def__init__(self, start=0, step=1):        self.value = start        self.step = step        self.history = []def__call__(self):"""每次调用增加计数"""        self.history.append(self.value)        self.value += self.stepreturn self.value - self.step  # 返回增加前的值defreset(self):"""重置计数器"""        self.value = 0        self.history.clear()def__str__(self):returnf"计数器: 当前值={self.value}, 步长={self.step}, 历史={self.history[-5:] if self.history else []}"def__repr__(self):returnf"Counter(start={self.value - len(self.history) * self.step}, step={self.step})"# 使用counter = Counter(start=10, step=2)print(counter())  # 10print(counter())  # 12print(counter())  # 14print(counter)    # 计数器: 当前值=16, 步长=2, 历史=[10, 12, 14]# 实际应用:函数装饰器import timefrom functools import wrapsclassTimer:"""计时装饰器"""def__init__(self, name=""):        self.name = name        self.total_time = 0        self.call_count = 0def__call__(self, func):        @wraps(func)defwrapper(*args, **kwargs):            start = time.perf_counter()            result = func(*args, **kwargs)            elapsed = time.perf_counter() - start            self.total_time += elapsed            self.call_count += 1if self.name:                print(f"[{self.name}{func.__name__} 耗时: {elapsed:.6f}秒")else:                print(f"{func.__name__} 耗时: {elapsed:.6f}秒")return resultreturn wrapper    @propertydefaverage_time(self):"""平均执行时间"""return self.total_time / self.call_count if self.call_count > 0else0# 使用装饰器@Timer("Fibonacci")deffibonacci(n):if n <= 1:return nreturn fibonacci(n-1) + fibonacci(n-2)print(fibonacci(10))  # 55,并打印每次调用耗时

2.7 上下文管理协议

__enter__ 和 __exit__ 方法:资源安全管理

import sqlite3import threadingfrom contextlib import ContextDecoratorclassDatabaseTransaction:"""数据库事务上下文管理器"""def__init__(self, db_path):        self.db_path = db_path        self.connection = None        self.cursor = Nonedef__enter__(self):"""进入上下文:建立连接"""        self.connection = sqlite3.connect(self.db_path)        self.cursor = self.connection.cursor()        print(f"已连接到数据库: {self.db_path}")return self.cursordef__exit__(self, exc_type, exc_val, exc_tb):"""退出上下文:提交或回滚"""if exc_type isNone:# 没有异常,提交事务            self.connection.commit()            print("事务已提交")else:# 有异常,回滚事务            self.connection.rollback()            print(f"事务已回滚,原因: {exc_val}")# 清理资源if self.cursor:            self.cursor.close()if self.connection:            self.connection.close()            print("数据库连接已关闭")# 返回False让异常继续传播returnFalseclassThreadSafeLock(ContextDecorator):"""线程锁上下文管理器,也可用作装饰器"""def__init__(self, lock=None):        self.lock = lock or threading.Lock()        self.lock_name = getattr(lock, 'name''未命名锁')def__enter__(self):        print(f"获取锁: {self.lock_name}")        self.lock.acquire()return selfdef__exit__(self, exc_type, exc_val, exc_tb):        print(f"释放锁: {self.lock_name}")        self.lock.release()returnFalsedef__call__(self, func):"""支持作为装饰器使用"""defwrapper(*args, **kwargs):with self:return func(*args, **kwargs)return wrapper# 使用示例print("=== 数据库事务示例 ===")try:with DatabaseTransaction(":memory:"as cursor:        cursor.execute("CREATE TABLE users (id INTEGER, name TEXT)")        cursor.execute("INSERT INTO users VALUES (1, 'Alice')")        cursor.execute("INSERT INTO users VALUES (2, 'Bob')")# 模拟异常# raise ValueError("测试异常")        cursor.execute("SELECT * FROM users")        print("查询结果:", cursor.fetchall())except Exception as e:    print(f"捕获到异常: {e}")print("\n=== 线程锁示例 ===")shared_counter = 0lock = threading.Lock()@ThreadSafeLock(lock)defincrement_counter():global shared_counter# 这个函数会在锁的保护下执行    shared_counter += 1    print(f"计数器: {shared_counter}")# 多线程测试threads = []for i in range(5):    t = threading.Thread(target=increment_counter)    threads.append(t)    t.start()for t in threads:    t.join()print(f"最终计数器值: {shared_counter}")

提示

  • 对象生命周期方法:__new____init____del__
  • 字符串表示:__str__(用户友好)、__repr__(开发者精确)
  • 比较运算:实现 __eq__ 和 __lt__,可使用 @total_ordering
  • 哈希运算:__eq__ 和 __hash__ 必须配对实现
  • 数值运算:支持各种数学运算符
  • 容器协议:让对象支持 len()、索引、迭代等操作
  • 可调用对象:__call__ 让实例像函数一样使用
  • 上下文管理:__enter__/__exit__ 实现资源自动管理

3. 何时使用魔法方法

3.1 判断标准:你真的需要魔法方法吗?

需要使用魔法方法的情况

  1. 自定义对象显示

    classProduct:def__init__(self, name, price):        self.name = name        self.price = price# 需要:让print(product)显示有用信息def__str__(self):returnf"{self.name}: ¥{self.price:.2f}"def__repr__(self):returnf"Product({self.name!r}{self.price})"
  2. 自定义相等比较

    classStudent:def__init__(self, student_id, name):        self.student_id = student_id        self.name = name# 需要:按学号比较学生是否相同def__eq__(self, other):return self.student_id == other.student_iddef__hash__(self):return hash(self.student_id)
  3. 创建数值类型

    classComplexNumber:def__init__(self, real, imag):        self.real = real        self.imag = imag# 需要:支持复数运算def__add__(self, other):return ComplexNumber(            self.real + other.real,            self.imag + other.imag        )
  4. 创建容器类

    classShoppingCart:def__init__(self):        self.items = []        self.quantities = {}# 需要:让购物车支持len()和迭代def__len__(self):return sum(self.quantities.values())def__iter__(self):return iter(self.items)

不需要使用魔法方法的情况

  1. 简单的数据容器 → 使用 @dataclass

    from dataclasses import dataclass, fieldfrom typing import List@dataclass(order=True)  # 自动生成比较方法classPoint:    x: float    y: float    tags: List[str] = field(default_factory=list)# 自动生成 __init__, __repr__, __eq__, __lt__ 等p1 = Point(1.02.0)p2 = Point(1.02.0)print(p1 == p2)  # True
  2. 只读数据结构 → 使用 NamedTuple

    from typing import NamedTupleclassCoordinate(NamedTuple):    x: float    y: float    z: float = 0.0# 默认值# 自动实现 __eq__, __hash__, __repr__ 等coord = Coordinate(1.02.0)print(coord.x)  # 1.0print(coord)    # Coordinate(x=1.0, y=2.0, z=0.0)
  3. 行为简单的类 → 使用普通类

    # 不需要魔法方法的情况classConfig:"""简单的配置类,不需要特殊行为"""def__init__(self, **kwargs):for key, value in kwargs.items():            setattr(self, key, value)config = Config(host="localhost", port=8080)print(config.host)  # localhost

3.2 应用建议

渐进式实现策略

classSmartVector:"""智能向量类 - 渐进式实现示例"""# 第一步:基本初始化def__init__(self, x, y):        self.x = x        self.y = y# 第二步:调试表示(尽早实现)def__repr__(self):returnf"SmartVector({self.x}{self.y})"# 第三步:用户友好显示(可选但推荐)def__str__(self):returnf"向量({self.x}{self.y})"# 第四步:相等比较(如果需要)def__eq__(self, other):ifnot isinstance(other, SmartVector):returnNotImplementedreturn self.x == other.x and self.y == other.y# 第五步:哈希支持(如果需要作为字典键)def__hash__(self):return hash((self.x, self.y))# 第六步:其他功能(按需添加)def__add__(self, other):ifnot isinstance(other, SmartVector):returnNotImplementedreturn SmartVector(self.x + other.x, self.y + other.y)def__abs__(self):import mathreturn math.hypot(self.x, self.y)

错误处理与健壮性

classSafeFraction:"""安全的分数类,包含完整错误处理"""def__init__(self, numerator, denominator):# 类型检查ifnot isinstance(numerator, (int, float)):raise TypeError(f"分子必须是数字,不是 {type(numerator).__name__}")ifnot isinstance(denominator, (int, float)):raise TypeError(f"分母必须是数字,不是 {type(denominator).__name__}")# 值检查if denominator == 0:raise ValueError("分母不能为零")# 规范化        self.numerator = numerator        self.denominator = denominator        self._normalize()def_normalize(self):"""化简分数"""import math        gcd = math.gcd(int(self.numerator), int(self.denominator))        self.numerator //= gcd        self.denominator //= gcd# 确保分母为正if self.denominator < 0:            self.numerator = -self.numerator            self.denominator = -self.denominatordef__add__(self, other):"""加法运算,包含类型检查"""ifnot isinstance(other, SafeFraction):# 尝试转换为分数try:                other = SafeFraction(other, 1)except (TypeError, ValueError):returnNotImplemented# 让Python尝试其他方式        new_num = self.numerator * other.denominator + other.numerator * self.denominator        new_den = self.denominator * other.denominatorreturn SafeFraction(new_num, new_den)def__str__(self):returnf"{self.numerator}/{self.denominator}"def__repr__(self):returnf"SafeFraction({self.numerator}{self.denominator})"# 测试健壮性try:    f1 = SafeFraction(12)    f2 = SafeFraction(34)    print(f1 + f2)  # 5/4    print(f1 + 2)   # 5/2# f3 = SafeFraction(1, 0)  # ValueError: 分母不能为零# f4 = SafeFraction("a", 2)  # TypeError: 分子必须是数字except Exception as e:    print(f"错误: {e}")

一致性原则

  • 如果实现了 __eq__,也应该实现 __hash__
  • 如果实现了 __lt__,考虑使用 @total_ordering
  • 容器类应该实现完整协议(__len____getitem____iter__等)
from functools import total_ordering@total_orderingclassConsistentClass:"""保持一致的魔法方法实现"""def__init__(self, value):        self.value = valuedef__eq__(self, other):ifnot isinstance(other, ConsistentClass):returnNotImplementedreturn self.value == other.valuedef__lt__(self, other):ifnot isinstance(other, ConsistentClass):returnNotImplementedreturn self.value < other.valuedef__hash__(self):return hash(self.value)def__repr__(self):returnf"ConsistentClass({self.value})"

提示

  • 魔法方法应按需实现,不要过度设计
  • 简单数据类优先使用 @dataclass 或 NamedTuple
  • 实现魔法方法时要考虑错误处理和边界情况
  • 保持相关方法的一致性实现

4. 应用示例

4.1 案例一:实现一个简单的分数类

import mathfrom functools import total_ordering@total_orderingclassFraction:"""完整的分数类实现,支持所有基本运算"""def__init__(self, numerator, denominator=1):"""        初始化分数        :param numerator: 分子        :param denominator: 分母(不能为0)        """ifnot isinstance(numerator, (int, float)):raise TypeError("分子必须是数字")ifnot isinstance(denominator, (int, float)):raise TypeError("分母必须是数字")if denominator == 0:raise ValueError("分母不能为零")# 转换为整数if isinstance(numerator, float):# 处理浮点数:先找到合适的分母            numerator, denominator = self._float_to_fraction(numerator, denominator)# 化简分数        self._reduce(numerator, denominator)def_float_to_fraction(self, numerator, denominator, precision=1e-6):"""将浮点数转换为分数近似"""from fractions import Fraction as PyFraction        frac = PyFraction(numerator / denominator).limit_denominator()return frac.numerator, frac.denominatordef_reduce(self, numerator, denominator):"""化简分数到最简形式"""# 使用math.gcd计算最大公约数        gcd_val = math.gcd(abs(int(numerator)), abs(int(denominator)))        self.numerator = numerator // gcd_val        self.denominator = denominator // gcd_val# 确保分母为正if self.denominator < 0:            self.numerator = -self.numerator            self.denominator = -self.denominator# 算术运算方法def__add__(self, other):        other = self._ensure_fraction(other)        new_num = self.numerator * other.denominator + other.numerator * self.denominator        new_den = self.denominator * other.denominatorreturn Fraction(new_num, new_den)def__sub__(self, other):        other = self._ensure_fraction(other)        new_num = self.numerator * other.denominator - other.numerator * self.denominator        new_den = self.denominator * other.denominatorreturn Fraction(new_num, new_den)def__mul__(self, other):        other = self._ensure_fraction(other)return Fraction(self.numerator * other.numerator,                        self.denominator * other.denominator)def__truediv__(self, other):        other = self._ensure_fraction(other)if other.numerator == 0:raise ZeroDivisionError("除以零")return Fraction(self.numerator * other.denominator,                       self.denominator * other.numerator)def__pow__(self, power):"""支持分数幂运算"""if isinstance(power, int):return Fraction(self.numerator ** power, self.denominator ** power)elif isinstance(power, float):# 返回浮点数结果return float(self) ** powerelse:returnNotImplemented# 比较运算方法def__eq__(self, other):        other = self._ensure_fraction(other)return (self.numerator == other.numerator and                self.denominator == other.denominator)def__lt__(self, other):        other = self._ensure_fraction(other)return self.numerator * other.denominator < other.numerator * self.denominator# 类型转换方法def__float__(self):return self.numerator / self.denominatordef__int__(self):return self.numerator // self.denominatordef__bool__(self):return self.numerator != 0# 字符串表示方法def__str__(self):if self.denominator == 1:return str(self.numerator)returnf"{self.numerator}/{self.denominator}"def__repr__(self):returnf"Fraction({self.numerator}{self.denominator})"def__format__(self, format_spec):"""支持格式化输出"""if format_spec == 'decimal':returnf"{float(self):.4f}"elif format_spec == 'mixed':            whole = self.numerator // self.denominator            remainder = abs(self.numerator) % self.denominatorif whole == 0:return str(self)elif remainder == 0:return str(whole)else:returnf"{whole}{remainder}/{self.denominator}"else:return str(self)# 辅助方法def_ensure_fraction(self, value):"""确保输入是Fraction类型"""if isinstance(value, (int, float)):return Fraction(value)elif isinstance(value, Fraction):return valueelse:raise TypeError(f"不支持的类型: {type(value)}")# 属性访问    @propertydefdecimal(self):"""获取小数形式"""return float(self)    @propertydefreciprocal(self):"""获取倒数"""return Fraction(self.denominator, self.numerator)# 类方法    @classmethoddeffrom_float(cls, value, max_denominator=1000):"""从浮点数创建分数"""from fractions import Fraction as PyFraction        frac = PyFraction(value).limit_denominator(max_denominator)return cls(frac.numerator, frac.denominator)# 使用示例print("=== 分数类使用示例 ===")f1 = Fraction(12)f2 = Fraction(34)print(f"f1 = {f1}, f2 = {f2}")print(f"f1 + f2 = {f1 + f2}")          # 5/4print(f"f1 - f2 = {f1 - f2}")          # -1/4print(f"f1 * f2 = {f1 * f2}")          # 3/8print(f"f1 / f2 = {f1 / f2}")          # 2/3print(f"f1 ** 2 = {f1 ** 2}")          # 1/4print(f"f1 < f2: {f1 < f2}")           # Trueprint(f"f1 的小数形式: {f1.decimal:.3f}")  # 0.500print(f"f1 的倒数: {f1.reciprocal}")    # 2/1print(f"格式化输出: {f2:mixed}")         # 0 3/4print(f"从浮点数创建: {Fraction.from_float(0.333)}")  # 333/1000# 混合类型运算print(f"f1 + 2 = {f1 + 2}")            # 5/2print(f"3 * f2 = {3 * f2}")            # 9/4 (使用__rmul__)

4.2 案例二:实现一个智能配置文件类

import jsonimport yaml  # 需要安装: pip install pyyamlfrom pathlib import Pathfrom typing import Any, Dict, Optional, UnionclassConfig:"""    智能配置文件类    支持JSON/YAML格式,提供字典和属性两种访问方式    """def__init__(self,                  filepath: Union[str, Path] = "config.json",                 auto_save: bool = True,                 default_config: Optional[Dict] = None):"""        初始化配置管理器        :param filepath: 配置文件路径        :param auto_save: 是否自动保存修改        :param default_config: 默认配置        """        self.filepath = Path(filepath)        self.auto_save = auto_save        self._data = {}# 加载或初始化配置if self.filepath.exists():            self._load()elif default_config:            self._data.update(default_config)if auto_save:                self.save()# 跟踪修改        self._modified = Falsedef_detect_format(self) -> str:"""检测文件格式"""        suffix = self.filepath.suffix.lower()if suffix in ['.json']:return'json'elif suffix in ['.yaml''.yml']:return'yaml'else:# 默认使用JSONreturn'json'def_load(self):"""加载配置文件"""        fmt = self._detect_format()try:with open(self.filepath, 'r', encoding='utf-8'as f:if fmt == 'json':                    self._data = json.load(f)elif fmt == 'yaml':                    self._data = yaml.safe_load(f)except (json.JSONDecodeError, yaml.YAMLError) as e:            print(f"配置文件解析错误: {e}")            self._data = {}except Exception as e:            print(f"加载配置文件失败: {e}")            self._data = {}defsave(self, filepath: Optional[Union[str, Path]] = None):"""保存配置文件"""        save_path = Path(filepath) if filepath else self.filepath        fmt = self._detect_format() if filepath isNoneelse self._detect_format_from_path(save_path)try:# 确保目录存在            save_path.parent.mkdir(parents=True, exist_ok=True)with open(save_path, 'w', encoding='utf-8'as f:if fmt == 'json':                    json.dump(self._data, f, indent=2, ensure_ascii=False)elif fmt == 'yaml':                    yaml.dump(self._data, f, default_flow_style=False, allow_unicode=True)            self._modified = False            print(f"配置已保存到: {save_path}")returnTrueexcept Exception as e:            print(f"保存配置文件失败: {e}")returnFalsedef_detect_format_from_path(self, path: Path) -> str:"""从路径检测格式"""        suffix = path.suffix.lower()return'yaml'if suffix in ['.yaml''.yml'else'json'# ============ 魔法方法实现 ============# 字典式访问def__getitem__(self, key: str) -> Any:"""支持 config['key'] 语法"""        keys = key.split('.')        value = self._datafor k in keys:if isinstance(value, dict) and k in value:                value = value[k]else:raise KeyError(f"配置键 '{key}' 不存在")return valuedef__setitem__(self, key: str, value: Any):"""支持 config['key'] = value 语法,支持点号路径"""        keys = key.split('.')        data = self._data# 导航到目标字典(创建中间路径)for k in keys[:-1]:if k notin data ornot isinstance(data[k], dict):                data[k] = {}            data = data[k]# 设置值        data[keys[-1]] = value        self._modified = Trueif self.auto_save:            self.save()def__delitem__(self, key: str):"""支持 del config['key'] 语法"""        keys = key.split('.')        data = self._datafor k in keys[:-1]:if k notin data:raise KeyError(f"配置键 '{key}' 不存在")            data = data[k]if keys[-1in data:del data[keys[-1]]            self._modified = Trueif self.auto_save:                self.save()else:raise KeyError(f"配置键 '{key}' 不存在")def__contains__(self, key: str) -> bool:"""支持 'key' in config 语法"""try:            _ = self[key]returnTrueexcept KeyError:returnFalsedefget(self, key: str, default: Any = None) -> Any:"""安全的获取方法"""try:return self[key]except KeyError:return default# 容器协议def__len__(self) -> int:"""支持 len(config)"""return len(self._data)def__iter__(self):"""支持迭代(返回顶层键)"""return iter(self._data)defkeys(self):"""返回所有键"""return self._data.keys()defvalues(self):"""返回所有值"""return self._data.values()defitems(self):"""返回所有键值对"""return self._data.items()# 属性式访问def__getattr__(self, name: str) -> Any:"""支持 config.key 语法"""if name in self._data:            value = self._data[name]# 如果是字典,返回包装对象以支持链式属性访问if isinstance(value, dict):return DictProxy(value, parent=self, path=name)return valueraise AttributeError(f"配置项 '{name}' 不存在")def__setattr__(self, name: str, value: Any):"""支持 config.key = value 语法"""if name in ('filepath''_data''auto_save''_modified'):# 特殊属性            super().__setattr__(name, value)else:            self[name] = valuedef__delattr__(self, name: str):"""支持 del config.key 语法"""if name in self._data:del self[name]else:            super().__delattr__(name)# 字符串表示def__str__(self) -> str:returnf"Config({self.filepath.name}): {len(self)} 个配置项"def__repr__(self) -> str:returnf"Config(filepath={self.filepath!r}, auto_save={self.auto_save})"def__enter__(self):"""上下文管理器支持"""return selfdef__exit__(self, exc_type, exc_val, exc_tb):"""退出上下文时自动保存"""if self._modified:            self.save()returnFalse# 实用方法defupdate(self, other: Dict):"""更新配置(类似字典的update)"""        self._data.update(other)        self._modified = Trueif self.auto_save:            self.save()defclear(self):"""清空配置"""        self._data.clear()        self._modified = Trueif self.auto_save:            self.save()defreload(self):"""重新加载配置文件"""if self.filepath.exists():            self._load()            self._modified = False            print(f"配置已重新加载")else:            print(f"配置文件不存在: {self.filepath}")    @propertydefmodified(self) -> bool:"""配置是否已被修改"""return self._modifiedclassDictProxy:"""字典代理,支持链式属性访问"""def__init__(self, data: Dict, parent=None, path=""):        self._data = data        self._parent = parent        self._path = f"{path}."if path else""def__getattr__(self, name: str) -> Any:if name in self._data:            value = self._data[name]if isinstance(value, dict):return DictProxy(value, self._parent, f"{self._path}{name}")return valueraise AttributeError(f"键 '{name}' 不存在")def__getitem__(self, key: str) -> Any:return self._data[key]def__repr__(self) -> str:returnf"DictProxy({self._data})"# 使用示例print("\n=== 智能配置类使用示例 ===")# 创建配置管理器config = Config("my_config.json", auto_save=True)# 多种方式设置配置config['app.name'] = "MyApp"# 字典式,支持点号路径config['app.version'] = "1.0.0"config['database.host'] = "localhost"config['database.port'] = 5432config['features'] = {"dark_mode"True"notifications"False}# 属性式访问config.theme = "dark"config.language = "zh-CN"print(f"应用名称: {config['app.name']}")      # MyAppprint(f"数据库主机: {config.database.host}")  # localhost (链式属性访问!)print(f"主题: {config.theme}")                # dark# 检查存在性print(f"'app.name' in config: {'app.name'in config}")  # Trueprint(f"'missing.key' in config: {'missing.key'in config}")  # False# 安全获取print(f"获取不存在的键: {config.get('missing.key''默认值')}")  # 默认值# 迭代配置print("\n所有配置项:")for key in config:    print(f"  {key}{config[key]}")# 删除配置del config['database.port']print(f"删除后数据库端口: {config.get('database.port''未设置')}")  # 未设置# 上下文管理器with Config("temp_config.yaml"as temp_config:    temp_config['temp.value'] = "临时值"    temp_config['another.value'] = 123# 退出上下文时自动保存# 重新加载config.reload()print(f"\n配置项数量: {len(config)}")# 导出为字典print(f"原始数据: {config._data}")

5. 常见问题

5.1 常见陷阱

陷阱1:__init__ 不是真正的构造函数

classMisunderstandingInit:def__init__(self):        print("__init__ 被调用")# 错误尝试:__init__ 不能返回非None值# return "something"  # TypeError: __init__() should return None    @classmethoddefcreate(cls):"""正确的创建方法"""        instance = cls.__new__(cls)if instance isnotNone:            instance.__init__()return instanceclassCorrectApproach:def__new__(cls, create_special=False):        print("__new__ 被调用 - 真正的构造函数")if create_special:# __new__可以返回不同类型的对象return"这是一个字符串,不是CorrectApproach实例"return super().__new__(cls)def__init__(self):        print("__init__ 被调用 - 初始化已创建的对象")# 测试obj1 = CorrectApproach()  # 正常创建obj2 = CorrectApproach(create_special=True)  # 返回字符串print(f"obj2类型: {type(obj2)}, 值: {obj2}")

陷阱2:__eq__ 和 __hash__ 必须配对实现

classUnhashableError:"""错误的类:有__eq__但没有__hash__"""def__init__(self, value):        self.value = valuedef__eq__(self, other):return self.value == other.value# 缺少 __hash__ 方法!# Python 3中这会使得对象不可哈希classCorrectHashable:"""正确的类:__eq__和__hash__配对"""def__init__(self, value):        self.value = valuedef__eq__(self, other):return self.value == other.valuedef__hash__(self):return hash(self.value)def__repr__(self):returnf"CorrectHashable({self.value})"# 测试try:    bad1 = UnhashableError(1)    bad2 = UnhashableError(1)# hash(bad1)  # 报错: TypeError: unhashable type: 'UnhashableError'# {bad1, bad2}  # 报错    print("UnhashableError 对象不可哈希")except TypeError as e:    print(f"错误: {e}")# 正确的使用good1 = CorrectHashable(1)good2 = CorrectHashable(1)good3 = CorrectHashable(2)print(f"good1 == good2: {good1 == good2}")  # Trueprint(f"hash(good1) == hash(good2): {hash(good1) == hash(good2)}")  # True# 可以作为字典键和集合元素dict_with_hashable = {good1: "值1", good3: "值3"}set_with_hashable = {good1, good2, good3}  # 只有good1和good3,good2与good1相等print(f"字典: {dict_with_hashable}")print(f"集合大小: {len(set_with_hashable)}")  # 2

陷阱3:无限递归调用

classInfiniteRecursion:"""危险的__getattribute__实现"""def__init__(self):        self.value = 42def__getattribute__(self, name):# 错误:会导致无限递归!# return self.name  # 调用self.name又会触发__getattribute__# 正确:使用object.__getattribute__return object.__getattribute__(self, name)defsafe_get_attribute(self, name):"""安全的属性获取方法"""return object.__getattribute__(self, name)classSafeClass:"""安全的属性访问"""def__init__(self):        self._data = {"x"1"y"2}def__getattr__(self, name):"""只有在属性不存在时才调用"""if name in self._data:return self._data[name]raise AttributeError(f"属性 '{name}' 不存在")def__setattr__(self, name, value):"""属性设置"""if name == '_data':            super().__setattr__(name, value)else:            self._data[name] = value# 测试安全类safe = SafeClass()print(f"safe.x: {safe.x}")  # 1print(f"safe.y: {safe.y}")  # 2safe.z = 3print(f"safe.z: {safe.z}")  # 3

陷阱4:修改不可变对象

classImmutablePoint:"""设计为不可变对象"""    __slots__ = ('_x''_y')  # 限制属性def__init__(self, x, y):        super().__setattr__('_x', x)        super().__setattr__('_y', y)    @propertydefx(self):return self._x    @propertydefy(self):return self._ydef__setattr__(self, name, value):"""禁止修改属性"""raise AttributeError(f"{self.__class__.__name__} 对象是只读的")def__eq__(self, other):return self._x == other._x and self._y == other._ydef__hash__(self):return hash((self._x, self._y))def__repr__(self):returnf"ImmutablePoint({self._x}{self._y})"# 测试不可变性point = ImmutablePoint(12)print(f"点坐标: ({point.x}{point.y})")try:    point.x = 3# 报错except AttributeError as e:    print(f"预期错误: {e}")# 可以作为字典键points_map = {point: "点A"}print(f"点是否在字典中: {point in points_map}")  # True

5.2 调试技巧

技巧1:使用 __dict__ 查看对象内部

classDebuggableClass:def__init__(self, name, value, **kwargs):        self.name = name        self.value = value# 动态添加额外属性for key, val in kwargs.items():            setattr(self, key, val)# 私有属性        self._secret = "隐藏数据"        self.__very_private = "非常私有"definspect(self):"""查看对象内部状态"""        print(f"对象ID: {id(self)}")        print(f"类型: {type(self).__name__}")        print(f"模块: {self.__module__}")        print(f"类名: {self.__class__.__name__}")        print("\n所有属性 (通过__dict__):")for key, value in self.__dict__.items():            print(f"  {key}{value!r}")        print("\n所有属性 (通过dir):")        attrs = [attr for attr in dir(self) ifnot attr.startswith('__'ornot attr.endswith('__')]        print(f"  {attrs[:10]}...")  # 只显示前10个# 创建并检查对象obj = DebuggableClass("测试对象"100, extra1="额外1", extra2=3.14)obj.inspect()# 直接访问__dict__print(f"\n直接访问__dict__: {obj.__dict__}")# 获取私有属性名(名称修饰)print(f"\n名称修饰后的私有属性: {obj._DebuggableClass__very_private}")

技巧2:检查魔法方法实现

defanalyze_magic_methods(obj):"""分析对象的魔法方法实现情况"""import inspect    print(f"分析对象: {obj}")    print(f"类型: {type(obj).__name__}")# 获取所有魔法方法    all_magic_methods = ['__init__''__new__''__del__','__str__''__repr__''__format__','__eq__''__ne__''__lt__''__le__''__gt__''__ge__''__hash__','__bool__''__len__','__getitem__''__setitem__''__delitem__''__contains__','__iter__''__next__''__reversed__','__call__','__enter__''__exit__','__add__''__sub__''__mul__''__truediv__''__mod__''__pow__','__getattr__''__setattr__''__delattr__''__getattribute__','__dir__''__sizeof__'    ]    implemented = []    not_implemented = []for method in all_magic_methods:if hasattr(obj.__class__, method):# 获取方法来源            cls_method = getattr(obj.__class__, method)if (inspect.isfunction(cls_method) or                inspect.ismethod(cls_method) or                isinstance(cls_method, property)):                implemented.append(method)else:                not_implemented.append(method)else:            not_implemented.append(method)    print(f"\n已实现的魔法方法 ({len(implemented)}个):")for method in sorted(implemented):        cls_method = getattr(obj.__class__, method)        print(f"  ✓ {method}: 定义于 {cls_method.__module__}.{cls_method.__qualname__}")    print(f"\n未实现的魔法方法 ({len(not_implemented)}个):")for method in sorted(not_implemented)[:10]:  # 只显示前10个        print(f"  ✗ {method}")if len(not_implemented) > 10:        print(f"  ... 还有 {len(not_implemented) - 10} 个")return implemented, not_implemented# 分析自定义类classSampleClass:def__init__(self, x):        self.x = xdef__str__(self):returnf"Sample({self.x})"def__eq__(self, other):return self.x == other.xobj = SampleClass(10)analyze_magic_methods(obj)

技巧3:使用 __slots__ 优化内存和调试

classWithSlots:"""使用__slots__优化内存和防止动态属性"""    __slots__ = ('x''y''_z')def__init__(self, x, y, z):        self.x = x        self.y = y        self._z = z  # 仍然可以有"私有"属性def__str__(self):returnf"WithSlots(x={self.x}, y={self.y}, z={self._z})"classWithoutSlots:"""不使用__slots__的传统类"""def__init__(self, x, y, z):        self.x = x        self.y = y        self._z = zdef__str__(self):returnf"WithoutSlots(x={self.x}, y={self.y}, z={self._z})"# 比较内存使用import sysobj_slots = WithSlots(123)obj_no_slots = WithoutSlots(123)print(f"WithSlots 内存大小: {sys.getsizeof(obj_slots) + sys.getsizeof(obj_slots.__dict__ if hasattr(obj_slots, '__dict__'else0)} 字节")print(f"WithoutSlots 内存大小: {sys.getsizeof(obj_no_slots) + sys.getsizeof(obj_no_slots.__dict__)} 字节")# 尝试添加动态属性obj_no_slots.new_attr = "动态添加"# 成功print(f"传统类可以添加动态属性: {hasattr(obj_no_slots, 'new_attr')}")try:    obj_slots.new_attr = "动态添加"# 失败    print("slots类可以添加动态属性")except AttributeError as e:    print(f"slots类不能添加动态属性: {e}")# slots类没有__dict__print(f"传统类有__dict__: {hasattr(obj_no_slots, '__dict__')}")print(f"slots类有__dict__: {hasattr(obj_slots, '__dict__')}")

技巧4:自定义异常信息

classValidatedVector:"""带验证的向量类,提供清晰的错误信息"""def__init__(self, x, y):        self._validate_input(x, "x")        self._validate_input(y, "y")        self.x = x        self.y = ydef_validate_input(self, value, name):"""验证输入值"""ifnot isinstance(value, (int, float)):raise TypeError(f"{self.__class__.__name__}.{name} 必须是数字,"f"但收到 {type(value).__name__} 类型: {value!r}"            )ifnot (0 <= value <= 100):  # 示例约束raise ValueError(f"{self.__class__.__name__}.{name} 必须在0-100之间,"f"但收到: {value}"            )def__add__(self, other):ifnot isinstance(other, ValidatedVector):raise TypeError(f"无法将 {type(other).__name__} 与 {self.__class__.__name__} 相加。"f"请提供另一个 {self.__class__.__name__} 实例。"            )return ValidatedVector(self.x + other.x, self.y + other.y)def__str__(self):returnf"ValidatedVector({self.x}{self.y})"# 测试清晰的错误信息try:    v1 = ValidatedVector(1020)    v2 = ValidatedVector(30"invalid")  # 会抛出清晰的错误except (TypeError, ValueError) as e:    print(f"清晰的错误信息:\n{e}")try:    v3 = ValidatedVector(1020)    result = v3 + "不是向量"# 类型错误except TypeError as e:    print(f"\n操作错误信息:\n{e}")

提示

  • 避免常见陷阱:理解 __new__ 和 __init__ 的区别,配对实现 __eq__ 和 __hash__
  • 使用 __dict__ 和 dir() 进行调试
  • 利用 __slots__ 优化内存使用和防止意外属性
  • 提供清晰的错误信息有助于调试
  • 使用 inspect 模块分析类的结构

6. 魔法方法分类速查表与进阶实践

6.1 魔法方法分类速查表

类别
方法
触发条件
说明
构造/析构__new__(cls, ...)MyClass()
创建实例,真正的构造函数
__init__(self, ...)obj = MyClass()
初始化实例
__del__(self)del obj
 或垃圾回收
析构函数(不可靠)
字符串表示__str__(self)str(obj)
print(obj)
用户友好字符串
__repr__(self)repr(obj)
, 交互式环境
开发者精确字符串
__format__(self, format_spec)format(obj, spec)
格式化输出
__bytes__(self)bytes(obj)
字节表示
比较运算__eq__(self, other)==
等于
__ne__(self, other)!=
不等于
__lt__(self, other)<
小于
__le__(self, other)<=
小于等于
__gt__(self, other)>
大于
__ge__(self, other)>=
大于等于
__hash__(self)hash(obj)
, 字典键
哈希值
数值运算__add__(self, other)+
加法
__sub__(self, other)-
减法
__mul__(self, other)*
乘法
__truediv__(self, other)/
真除法
__floordiv__(self, other)//
地板除法
__mod__(self, other)%
取模
__pow__(self, other)**
幂运算
__matmul__(self, other)@
矩阵乘法
__neg__(self)-obj
取负
__pos__(self)+obj
取正
__abs__(self)abs(obj)
绝对值
__invert__(self)~obj
按位取反
类型转换__bool__(self)bool(obj)
if obj:
布尔值
__int__(self)int(obj)
整数转换
__float__(self)float(obj)
浮点数转换
__complex__(self)complex(obj)
复数转换
__index__(self)operator.index()
索引转换
容器协议__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 x in obj
迭代器
__next__(self)next(iterator)
下一个元素
__reversed__(self)reversed(obj)
反向迭代
可调用对象__call__(self, ...)obj(...)
像函数一样调用
上下文管理__enter__(self)with obj:
进入上下文
__exit__(self, exc_type, ...)
退出 with 块
退出上下文
属性访问__getattr__(self, name)obj.name
 (属性不存在)
获取缺失属性
__getattribute__(self, name)obj.name
 (总是调用)
获取属性
__setattr__(self, name, value)obj.name = value
设置属性
__delattr__(self, name)del obj.name
删除属性
__dir__(self)dir(obj)
属性列表
描述符协议__get__(self, instance, owner)
描述符获取
描述符协议
__set__(self, instance, value)
描述符设置
描述符协议
__delete__(self, instance)
描述符删除
描述符协议
复制协议__copy__(self)copy.copy(obj)
浅拷贝
__deepcopy__(self, memo)copy.deepcopy(obj)
深拷贝
异步支持__aiter__(self)async for
异步迭代器
__anext__(self)async for
 的下一个
异步下一个
__aenter__(self)async with
异步上下文进入
__aexit__(self, ...)
退出 async with
异步上下文退出
__await__(self)await obj
可等待对象

6.2 与内置装饰器结合使用

使用 @dataclass 自动生成魔法方法

from dataclasses import dataclass, field, asdict, astuplefrom typing import List, Optional@dataclass(order=True, frozen=True)  # order=True生成比较方法,frozen=True使实例不可变classPerson:"""使用dataclass自动生成__init__, __repr__, __eq__等"""    name: str    age: int    email: Optional[str] = None    hobbies: List[str] = field(default_factory=list)# 可以自定义方法defis_adult(self) -> bool:return self.age >= 18# 可以自定义属性    @propertydefdisplay_name(self) -> str:returnf"{self.name} ({self.age})"# 自动生成的功能p1 = Person("Alice"25"alice@example.com", ["reading""hiking"])p2 = Person("Bob"30)p3 = Person("Alice"25)print(f"p1: {p1}")                     # 自动生成 __repr__print(f"p1 == p3: {p1 == p3}")         # True (自动生成 __eq__)print(f"p1 < p2: {p1 < p2}")           # True (按字段顺序比较)print(f"p1.is_adult(): {p1.is_adult()}")  # Trueprint(f"p1.display_name: {p1.display_name}")  # Alice (25)# 转换为字典和元组print(f"asdict: {asdict(p1)}")print(f"astuple: {astuple(p1)}")# frozen=True使实例不可变try:    p1.age = 26# 报错:dataclasses.FrozenInstanceErrorexcept Exception as e:    print(f"不能修改frozen对象: {e}")

使用 @total_ordering 简化比较操作

from functools import total_orderingimport math@total_orderingclassCircle:"""圆形类,使用total_ordering自动生成完整比较操作"""def__init__(self, radius):        self.radius = radius    @propertydefarea(self):return math.pi * self.radius ** 2    @propertydefcircumference(self):return2 * math.pi * self.radiusdef__eq__(self, other):ifnot isinstance(other, Circle):returnNotImplementedreturn self.radius == other.radiusdef__lt__(self, other):ifnot isinstance(other, Circle):returnNotImplementedreturn self.radius < other.radiusdef__str__(self):returnf"Circle(r={self.radius})"# 使用total_ordering自动获得所有比较操作c1 = Circle(5)c2 = Circle(10)c3 = Circle(5)print(f"c1 == c3: {c1 == c3}")  # Trueprint(f"c1 != c2: {c1 != c2}")  # Trueprint(f"c1 < c2: {c1 < c2}")    # Trueprint(f"c1 <= c2: {c1 <= c2}")  # Trueprint(f"c2 > c1: {c2 > c1}")    # Trueprint(f"c2 >= c1: {c2 >= c1}")  # True# 可以排序circles = [Circle(3), Circle(1), Circle(2)]circles.sort()print(f"排序后的圆: {[c.radius for c in circles]}")  # [1, 2, 3]

使用 @property 创建计算属性

classTemperature:"""温度类,使用property创建智能属性"""def__init__(self, celsius):        self._celsius = celsius    @propertydefcelsius(self):"""摄氏温度"""return self._celsius    @celsius.setterdefcelsius(self, value):"""设置摄氏温度,带有验证"""if value < -273.15:raise ValueError("温度不能低于绝对零度(-273.15°C)")        self._celsius = value    @propertydeffahrenheit(self):"""华氏温度(计算属性)"""return self._celsius * 9/5 + 32    @fahrenheit.setterdeffahrenheit(self, value):"""通过华氏温度设置摄氏温度"""        self._celsius = (value - 32) * 5/9    @propertydefkelvin(self):"""开氏温度(计算属性)"""return self._celsius + 273.15    @kelvin.setter  defkelvin(self, value):"""通过开氏温度设置摄氏温度"""        self._celsius = value - 273.15def__str__(self):returnf"{self.celsius:.1f}°C = {self.fahrenheit:.1f}°F = {self.kelvin:.1f}K"# 使用智能属性temp = Temperature(25)print(f"初始温度: {temp}")# 修改摄氏温度temp.celsius = 30print(f"修改后: {temp}")# 通过华氏温度设置temp.fahrenheit = 77print(f"设为77°F后: {temp}")# 通过开氏温度设置temp.kelvin = 300print(f"设为300K后: {temp}")# 属性验证try:    temp.celsius = -300# 低于绝对零度except ValueError as e:    print(f"验证错误: {e}")

使用 __slots__ 优化性能

import timeitfrom pympler import asizeof  # 需要安装: pip install pymplerclassRegularPoint:"""普通类,使用__dict__存储属性"""def__init__(self, x, y, z):        self.x = x        self.y = y        self.z = zdefdistance(self):return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** 0.5classSlotsPoint:"""使用__slots__优化的类"""    __slots__ = ('x''y''z')def__init__(self, x, y, z):        self.x = x        self.y = y        self.z = zdefdistance(self):return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** 0.5defbenchmark():"""性能对比基准测试"""    print("=== 内存使用对比 ===")# 创建大量对象    regular_points = [RegularPoint(i, i+1, i+2for i in range(1000)]    slots_points = [SlotsPoint(i, i+1, i+2for i in range(1000)]# 内存使用    regular_memory = sum(asizeof.asizeof(p) for p in regular_points[:10]) * 100# 估算    slots_memory = sum(asizeof.asizeof(p) for p in slots_points[:10]) * 100# 估算    print(f"1000个RegularPoint对象 ≈ {regular_memory / 1024:.1f} KB")    print(f"1000个SlotsPoint对象 ≈ {slots_memory / 1024:.1f} KB")    print(f"内存节省: {(regular_memory - slots_memory) / regular_memory * 100:.1f}%")    print("\n=== 属性访问速度对比 ===")# 属性访问速度测试    regular_point = RegularPoint(123)    slots_point = SlotsPoint(123)defaccess_regular():        _ = regular_point.x        regular_point.x = 10defaccess_slots():        _ = slots_point.x        slots_point.x = 10# 运行基准测试    regular_time = timeit.timeit(access_regular, number=1000000)    slots_time = timeit.timeit(access_slots, number=1000000)    print(f"RegularPoint属性访问: {regular_time:.3f}秒 (100万次)")    print(f"SlotsPoint属性访问: {slots_time:.3f}秒 (100万次)")    print(f"速度提升: {(regular_time - slots_time) / regular_time * 100:.1f}%")    print("\n=== 动态属性测试 ===")# 动态添加属性    regular_point.new_attr = "可以添加"    print(f"RegularPoint可以添加动态属性: {hasattr(regular_point, 'new_attr')}")try:        slots_point.new_attr = "不能添加"        print(f"SlotsPoint可以添加动态属性")except AttributeError:        print(f"SlotsPoint不能添加动态属性(这是设计特性)")if __name__ == "__main__":    benchmark()

使用描述符创建高级属性

classValidatedAttribute:"""描述符:验证属性值"""def__init__(self, min_value=None, max_value=None, allowed_types=None):        self.min_value = min_value        self.max_value = max_value        self.allowed_types = allowed_types        self.private_name = Nonedef__set_name__(self, owner, name):"""设置属性名"""        self.private_name = '_' + namedef__get__(self, obj, objtype=None):"""获取属性值"""if obj isNone:return selfreturn getattr(obj, self.private_name, None)def__set__(self, obj, value):"""设置属性值,带验证"""# 类型检查if self.allowed_types andnot isinstance(value, self.allowed_types):raise TypeError(f"属性必须是 {self.allowed_types} 类型")# 最小值检查if self.min_value isnotNoneand value < self.min_value:raise ValueError(f"属性值不能小于 {self.min_value}")# 最大值检查if self.max_value isnotNoneand value > self.max_value:raise ValueError(f"属性值不能大于 {self.max_value}")        setattr(obj, self.private_name, value)classPersonWithValidation:"""使用描述符验证属性的Person类"""# 使用描述符定义属性    age = ValidatedAttribute(min_value=0, max_value=150, allowed_types=(int,))    height = ValidatedAttribute(min_value=0.0, max_value=3.0, allowed_types=(int, float))    name = ValidatedAttribute(allowed_types=(str,))def__init__(self, name, age, height):        self.name = name  # 通过描述符设置        self.age = age    # 通过描述符设置        self.height = height  # 通过描述符设置def__str__(self):returnf"{self.name}{self.age}岁, {self.height:.2f}米"# 测试描述符验证try:    person = PersonWithValidation("张三"251.75)    print(f"创建成功: {person}")# 测试验证    person.age = 30# 正常    print(f"修改年龄后: {person}")    person.age = -5# 会触发验证错误except ValueError as e:    print(f"验证错误: {e}")try:    person.name = 123# 类型错误except TypeError as e:    print(f"类型错误: {e}")

使用元类控制类创建

classAutoRegisterMeta(type):"""元类:自动注册所有子类"""    registry = {}def__new__(mcs, name, bases, namespace):# 创建新类        cls = super().__new__(mcs, name, bases, namespace)# 如果不是基类,则注册if name != 'BaseModel':            mcs.registry[name] = cls            print(f"已注册类: {name}")return clsdef__init__(cls, name, bases, namespace):        super().__init__(name, bases, namespace)# 为类添加类属性        cls._registered = True# 自动添加__repr__方法if'__repr__'notin namespace:defauto_repr(self):                attrs = ', '.join(f"{k}={v!r}"for k, v in self.__dict__.items())returnf"{name}({attrs})"            cls.__repr__ = auto_reprclassBaseModel(metaclass=AutoRegisterMeta):"""基类,使用元类"""pass# 创建子类classUser(BaseModel):def__init__(self, name, age):        self.name = name        self.age = ageclassProduct(BaseModel):def__init__(self, name, price):        self.name = name        self.price = price# 测试print(f"\n已注册的类: {list(AutoRegisterMeta.registry.keys())}")user = User("Alice"25)product = Product("Book"29.99)print(f"\n自动生成的__repr__:")print(f"user: {user}")print(f"product: {product}")print(f"\n类属性:")print(f"User._registered: {User._registered}")print(f"Product._registered: {Product._registered}")

提示

  • 使用内置装饰器如 @dataclass@total_ordering 简化代码
  • 利用 @property 创建计算属性和验证逻辑
  • 使用 __slots__ 优化内存和性能
  • 通过描述符创建可重用的属性验证逻辑
  • 使用元类控制类的创建过程
  • 了解各种魔法方法的分类和用途

总结

  1. 魔法方法是 Python 的协议系统:通过实现特定方法,让自定义对象支持内置类型的操作

  2. 理解执行顺序

    • __new__:创建对象(真正的构造函数)
    • __init__:初始化对象
    • __del__:清理对象(不可依赖)
  3. 字符串表示原则

    • __repr__:给开发者,应能用于重建对象
    • __str__:给用户,应友好易读
  4. 比较运算必须配对实现

    • 实现 __eq__ 必须同时实现 __hash__
    • 实现 __lt__ 可以使用 @total_ordering 简化
  5. 容器协议应完整实现__len____getitem____setitem____iter__ 等

学习建议

初级阶段(掌握基础)

  1. __init__:对象初始化
  2. __str__ 和 __repr__:对象表示
  3. __eq__:相等比较
  4. __len__:支持 len() 函数

中级阶段(进阶应用)

  1. __getitem__ 和 __setitem__:支持索引操作
  2. __iter__ 和 __next__:支持迭代
  3. __call__:可调用对象
  4. __enter__ 和 __exit__:上下文管理器
  5. __add____sub__ 等:运算符重载

高级阶段(深入理解)

  1. __new__:对象创建控制
  2. __getattr__ 和 __getattribute__:属性访问控制
  3. __slots__:内存优化
  4. 描述符协议:高级属性控制
  5. 元类编程:类创建控制

专家阶段(系统设计)

  1. 完整协议实现
  2. 性能优化技巧
  3. 框架级设计
  4. 元编程应用

实用建议

1. 按需实现,避免过度设计

# 简单情况使用dataclassfrom dataclasses import dataclass@dataclassclassPoint:    x: float    y: float# 需要特殊行为时才自定义魔法方法classSpecialPoint:def__init__(self, x, y):        self.x = x        self.y = ydef__add__(self, other):return SpecialPoint(self.x + other.x, self.y + other.y)

2. 保持一致性

  • 如果实现了 __eq__,必须实现 __hash__
  • 如果实现了 __lt__,建议实现完整的比较方法
  • 容器类应实现完整的容器协议

3. 提供清晰的错误信息

classVector:def__add__(self, other):ifnot isinstance(other, Vector):raise TypeError(f"只能与Vector实例相加,但收到 {type(other).__name__}"            )# ...

4. 编写测试

import unittestclassTestVector(unittest.TestCase):deftest_addition(self):        v1 = Vector(12)        v2 = Vector(34)        result = v1 + v2        self.assertEqual(result.x, 4)        self.assertEqual(result.y, 6)deftest_invalid_addition(self):        v1 = Vector(12)with self.assertRaises(TypeError):            v1 + "不是向量"

5. 参考标准库实现

# 学习 collections.abc 中的抽象基类from collections.abc import Sequence, MutableSequenceclassMyList(MutableSequence):"""通过继承抽象基类实现完整协议"""def__init__(self):        self._data = []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)definsert(self, index, value):        self._data.insert(index, value)

常见场景

场景
需要的魔法方法
示例
创建不可变对象__slots__
__setattr__
配置类、常量类
实现数值类型__add__
__sub____mul__ 等
向量、矩阵、复数
创建容器类__len__
__getitem____iter__ 等
自定义列表、字典
实现上下文管理__enter__
__exit__
资源管理、锁、计时器
创建装饰器__call__
__init__
函数装饰器、类装饰器
实现状态机__call__
__getattr__
状态模式、策略模式
创建DSL
多种魔法方法组合
领域特定语言

一些思考

魔法方法不是魔法,而是 Python 设计哲学的体现

  1. 一致性:让自定义对象与内置类型行为一致
  2. 简洁性:通过特殊方法简化复杂操作
  3. 明确性:清晰的协议和约定
  4. 灵活性:允许开发者扩展语言功能

最好的代码是自解释的代码。当你合理使用魔法方法时,你的代码会变得更加:

  • 直观vector1 + vector2 比 vector_add(vector1, vector2) 更直观
  • 简洁for item in my_container 比手动迭代更简洁
  • 强大:自定义对象可以拥有内置类型的所有能力

通过掌握魔法方法,不仅可以学会 Python 的一个技术特性,更能深入理解 Python 的 "Pythonic" 设计哲学。这种理解将帮助我们在任何 Python 项目中编写更优雅、更强大、更易维护的代码。

喊一句口号:理解 → 实践 → 掌握 → 创新,不断积累,让自己更专业,让我们的 Python 代码变得更加 "Pythonic"!



作者简介:码上工坊,探索用编程为己赋能,定期分享编程知识和项目实战经验。持续学习、适应变化、记录点滴、复盘反思、成长进步。

重要提示:本文主要是记录自己的学习与实践过程,所提内容或者观点仅代表个人意见,只是我以为的,不代表完全正确,欢迎交流讨论。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-08 07:04:45 HTTP/2.0 GET : https://f.mffb.com.cn/a/468644.html
  2. 运行时间 : 0.285356s [ 吞吐率:3.50req/s ] 内存消耗:4,774.59kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=fc2dbe8549701e40ffc37fa5ea5d7c2f
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000973s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001512s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000712s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.001943s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001421s ]
  6. SELECT * FROM `set` [ RunTime:0.005699s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001487s ]
  8. SELECT * FROM `article` WHERE `id` = 468644 LIMIT 1 [ RunTime:0.001711s ]
  9. UPDATE `article` SET `lasttime` = 1770505485 WHERE `id` = 468644 [ RunTime:0.008774s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.004203s ]
  11. SELECT * FROM `article` WHERE `id` < 468644 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001196s ]
  12. SELECT * FROM `article` WHERE `id` > 468644 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.008422s ]
  13. SELECT * FROM `article` WHERE `id` < 468644 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.038473s ]
  14. SELECT * FROM `article` WHERE `id` < 468644 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.016731s ]
  15. SELECT * FROM `article` WHERE `id` < 468644 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.019247s ]
0.289447s