type
status
date
slug
summary
tags
category
icon
password
📝 面向对象的成员
1、变量(Data Members)
1.1、总览速查表
名称 | 定义位置 | 归属 | 访问方式 | 生命周期 |
类变量 | 类体里,方法外 | 类本身 | 类名.变量 或 实例.变量 | 程序运行期间 |
实例变量 | 方法内, self. 前缀 | 每个实例 | 实例.变量 | 实例创建→销毁 |
局部变量 | 方法/函数内部 | 栈帧 | 仅函数内部 | 调用→返回 |
私有变量 | __var 或 _var | 实例/类 | 类内 self.__var ,类外受限 | 同实例变量 |
1.2、类变量(Class Variable)
- 定义位置:位于 类体内部、所有方法外部(即直接写在类名下,且前面无
self.
)。
- 生命周期
- 创建:类被 定义/导入 时立即生成,直到程序结束才销毁。
- 销毁:进程结束或类被显式
del
时。
- 共享性
- 所有实例和类本身共享同一份内存。
- 通过 类名.变量 或 实例.变量 都可访问;实例修改会 创建同名实例变量 覆盖,但类变量本身不变。
1.3、实例变量(Instance Variable)
- 定义位置:在 实例方法内部 且通过
self.变量名 = 值
的形式绑定,通常放在__init__
中,也允许在其它方法里动态添加。
- 生命周期
- 创建:实例化对象(
obj = Class(...)
)时随__init__
或后续赋值产生。 - 销毁:该对象被垃圾回收(无引用)时自动释放。
- 隔离性
- 每个实例拥有独立的一份内存。
- 实例之间互不影响;修改一个实例变量不会影响其它实例。
1.4、局部变量(local variable)
- 定义位置:出现在类的方法体内部(包括实例方法、类方法、静态方法)或函数体内部,且前面没有
self.
或 类名前缀。
- 生命周期
- 创建:执行到变量赋值语句时。
- 销毁:方法/函数执行结束(return 或抛出异常)即被垃圾回收。
- 隔离性
- 每次方法调用都重新创建,不共享给其它调用或实例。
- 作用域仅限于当前栈帧,外部无法访问。
1.5、私有变量(private variable)
- 定义位置:在类的方法内部,以 双下划线前缀
__变量名
或 单下划线前缀_变量名
绑定到实例或类本身。 __var
(双下划线)触发「名称重整」,类外无法直接访问;_var
(单下划线)仅约定私有,仍可直接访问。
- 生命周期:与所属实例或类的生命周期一致
- 创建:实例化或类定义时赋值;
- 销毁:实例/类被垃圾回收时释放。
- 隔离性
- 作用域:仅在类内部可见(双下划线通过名称重整强制隔离)。
- 每实例/每类独立一份,与其他实例/类互不干扰。
1.6、五大易错点
- 同名掩盖:读时“就近”,写时“新建”
易错:误以为
a.x = 99
会修改类变量。__私有
名称重整:读/写都要用重整后的名字
子类内部读写双下划线变量,必须加
_父类__前缀
。- 继承链查找顺序:只读不改时遵循 MRO
若 写
C.x = 2
则会在 C 类本身 新建变量,不会改动 P.x
。- 实例写类变量:需显式指定类名
- 可变类变量陷阱:读引用,写重绑
若执行
Box.items = [7]
则重绑到新列表,旧列表与实例无影响。2、方法(Method Members)
2.1、总览速查表
方法类型 | 装饰器 | 默认第一个形参 | 归属 | 访问场景 | 典型用途 |
实例方法 | 无 | self (当前对象) | 实例 | 需要对象状态 | 读写属性、业务逻辑 |
类方法 | @classmethod | cls (当前类) | 类 | 无需对象但需类级数据 | 工厂方法、修改类变量 |
静态方法 | @staticmethod | 无 | 类 | 既不需要对象也不需要类 | 工具函数、校验逻辑 |
2.2、实例方法(Instance Method)
- 定义在类体内部、未加任何装饰器的普通函数;其第一个形参必须是
self
(名字可改,但约定为self
),代表当前对象本身。
- 绑定与调用创建:类被定义后,函数对象即被绑定为实例方法。调用:
- 由实例对象调用:
obj.method()
- 由类名手动调用:
Class.method(obj, *args)
(极少用)。
- 作用域与访问权限作用域:方法体内部可直接访问并修改实例变量(
self.var
)和类变量(Class.var
或self.__class__.var
)。无法访问其他实例的私有变量(除非显式传参)。
- 生命周期与实例对象同生同灭;实例销毁后,该方法仍可被类再次调用(但需新实例)。
- 示例
- 总结:实例方法以
self
为入口,只能通过对象调用,用于读写该对象自身状态或调用类级资源。
2.3、类方法(Class Method)
- 定义与语法用装饰器
@classmethod
声明,第一个形参必须是cls
(约定俗成),指向当前类对象本身。
- 绑定与调用绑定:类加载时自动把函数包装成类方法对象,与实例无关。调用方式
- 类名调用:
Foo.cm(x)
- 实例调用:
obj.cm(x)
(等价于Foo.cm(x)
,因为cls=Foo
)。
- 作用域与权限可读写类变量 (
cls.var
),无法直接访问实例变量(除非显式传入实例)。可继承:子类调用时cls
就是子类,天然支持多态。
- 生命周期与类同生同灭;类被垃圾回收后方法才消失。
- 典型用途工厂方法:根据参数返回不同实例。修改类级配置。类级工具函数。
- 示例:
- 总结:类方法以
cls
为入口,操作类本身或提供类级工厂,与具体实例无关,可被类或实例调用。
2.4、静态方法(@staticmethod)
- 定义用
@staticmethod
装饰,无默认形参(既无self
也无cls
),本质上只是一个放在类命名空间里的普通函数。
- 绑定与调用不绑定任何对象或类,不会自动传入
self
/cls
。调用方式 - 类名调用:
Foo.util(1,2)
- 实例调用:
obj.util(1,2)
(与类名调用等价)。
- 作用域与隔离性不能直接访问实例变量或类变量(除非显式传参)。仅借类的命名空间做逻辑归类,与类/实例状态完全解耦。
- 生命周期与类同生同灭;类被卸载时方法随之消失。
- 典型用途工具/校验函数(如日期格式检查)。与类逻辑相关、但无需类或实例状态的辅助方法。
- 示例
- 总结:静态方法既不依赖实例也不依赖类,只是借类的“文件夹”存放工具函数,调用时与普通函数无异。
3、属性(Property Members)
在 Python 面向对象中,“属性”可以分成两大实现路线:
- “定义变量”——最朴素的类属性 / 实例属性;
- “基于装饰器”——把方法伪装成属性(
@property
、@staticmethod
、@classmethod
等)。
3.1、基于定义变量:类属性 & 实例属性
- 类属性:所有实例共享;
- 实例属性:每个对象独立一份;
- 读写规则:读时“实例→类”,写时“实例新建同名变量”。
3.2、基于装饰器:把方法“伪装”成属性
@property
(只读属性 / 可读写属性)- 访问方式像变量(
c.area
),背后其实是方法调用; - 支持
getter
/setter
/deleter
,实现“数据验证 + 懒加载”。
@staticmethod
与@classmethod
(类级属性伪装)- 静态方法:纯粹工具函数,放在类命名空间内;
- 类方法:可读写类变量、充当工厂函数。
3.3、两条路线的对比速查表
特性 | 定义变量 | @property / @staticmethod / @classmethod |
语法 | attr = value | @decorator + 函数 |
是否可验证 | ❌ | ✅(getter/setter) |
是否可懒加载 | ❌ | ✅(计算属性) |
是否可读写类级数据 | 类变量可 | 类方法可 |
调用成本 | 低 | 方法调用,略高 |
- 想“存数据”——用定义变量(类 / 实例属性)
- 想“数据 + 逻辑封装”——用
@property
、@staticmethod
、@classmethod
把方法伪装成属性,实现验证、懒加载、工厂等高级行为。
3.4、拓展
property()
方法
作用:把
getter
/ setter
/ deleter
三个函数一次性封装成同名属性,实现「数据验证、懒加载、只读/只写」等高级封装,而不暴露内部存储。参数 | 默认值 | 描述 |
fget | None | 读属性时调用的函数(getter) |
fset | None | 写属性时调用的函数(setter) |
fdel | None | 删除属性时调用的函数(deleter) |
doc | None | 属性的文档字符串 |
@property
、@xxx.setter
、@xxx.deleter
装饰器
📝 面向对象的成员修饰符
1、成员修饰符速查表
可见级别 | 语法 | 内部/外部访问 | 机制 | 典型场景 |
公开 Public | name | ✅/✅ | 无限制 | 普通属性/方法 |
受保护 Protected | _name | ✅/✅(约定) | 仅 IDE/文档提示 | 子类继承用 |
私有 Private | __name | ✅/❌(改名) | 名称重整 _Class__name | 完全隐藏 |
只读属性 | @property | ✅读/❌写 | 方法伪装属性 | 计算/验证 |
类私有 | __name__ | ✅/✅ | 魔术方法 | 系统钩子 |
2、完整示例:
3、使用与陷阱演示
📝 对象嵌套
1、相关概念
1.1、一个类的属性 不是普通数字/字符串,而是另一个对象 —— 这就是嵌套。
1.2、三种常见套法:
套法 | 大白话 | 例子 | 生命周期 |
组合(整体-部分) | “我死你随” | 班级里有学生,班级解散,学生也没了 | 同生同死 |
关联 | “咱俩认识” | 学生持有班级引用,但班级可以换 | 可换可空 |
依赖 | “临时用一下” | 学生进班级前才 new Classes() | 用完即弃 |
2、一张图秒懂
3、代码对照
3.1、 组合(最紧密)
3.2、关联(松一点,可换班级)
3.3、依赖(最松,临时用)
4、生命周期对比表
关系 | 对象诞生 | 对象销毁 | 换对象 |
组合 | 整体 __init__ 里一起 new | 整体销毁,部分一起没 | 不支持 |
关联 | 外部 new 后传进来 | 整体销毁,部分仍活 | 随时换 |
依赖 | 方法里临时 new | 方法结束即没 | 每次新 |
📝 特殊成员
1、__init__
,初始化方法
1.1、规则一句话
子类没定义
__init__
⇒ 沿 MRO 找第一个定义了 __init__
的类,把它的实现拿来用;如果这一路谁都没写,最终落到 object.__init__
。1.2、代码验证
1.3、菱形继承再看 MRO
MRO:
D → B → C → A → object
1.4、小结
- 子类不写
__init__
≠ 不执行初始化;
- 解释器按 MRO 顺序复用第一个遇到的
__init__
;
- 参数列表必须和“被选中的那个
__init__
”完全匹配,否则调用方抛TypeError
;
- 想“自动转发全部参数”就自己在子类里写
super().__init__(*args, **kwargs)
。
2、__new__
,构造方法
new 是真正负责“造壳”的构造器,它创建实例并决定返回什么;init 只是给空壳“填肉”的初始化器。
2.1、定义与签名
2.2、调用顺序
2.3、典型用途
- 继承不可变类型(int、str、tuple)
- 单例模式
- 对象池 / 缓存
- 元类自定义实例化
2.4、示例
- 继承不可变类型
- 单例
2.5、注意
- 必须返回实例;返回
None
或类型不匹配会抛TypeError
。
- 仅当返回实例且类型正确时,
__init__
才会被调用。
3、__call__
3.1、作用与语法
让实例可以像函数一样被调用——实现 可调用对象(callable instance)。
3.2、定义示例
3.3、与函数的区别
- 可携带状态(如上例
count
)。
- 可继承、组合、多态,天然面向对象。
3.4、典型场景
- 计数器、缓存器、策略对象、装饰器类。
- 框架钩子(如
torch.nn.Module
的forward
实质由__call__
触发)。
3.5、判断与调试
3.6、总结
__call__
让实例“变身”函数,既能保存状态又能像函数一样使用,是 Python 实现可调用对象的魔法钩子。4、__str__
4.1、作用与语法
当对象被
print()
、str()
或 f-string 调用时,Python 自动触发 __str__
方法,返回面向终端用户的可读字符串。4.2、使用示例
4.3、与 repr 区别
- str:面向用户,简洁、美观。
- repr:面向开发者,通常返回能重建对象的表达式字符串。若只实现
__str__
,则repr(obj)
会回退到默认格式;反之亦然。
4.4、总结
实现
__str__
让你的对象在打印时直接呈现自定义、友好的文字信息。5、__dict__
5.1、作用
- 实例字典:保存实例的所有可写属性(不含 slots、描述符、类变量)。
- 类字典:保存类的属性、方法、类变量等。
- 本质是 普通 dict,可动态增删改查。
5.2、语法示例
5.3、常见操作
5.4、注意点
- 使用
__slots__
的类实例无__dict__
。
- 类变量不会出现在实例
__dict__
中。
- 修改
__dict__
会立即生效,但破坏封装,慎用于生产代码。
5.5、总结
__dict__
是实例/类的「属性仓库」,可读写、可遍历,是 Python 动态特性的核心接口之一。6、__getitem__
/__setitem__
/__delitem__
这三个魔法方法让自定义对象支持「索引/切片」语法,像列表、字典一样用
obj[key]
、obj[key] = value
、del obj[key]
。6.1、方法签名
6.2、最小可运行示例(列表包装器)
6.3、切片也走同一路径
obj[1:3]
等价于obj.__getitem__(slice(1, 3, None))
obj[1:3] = [4,5]
等价于obj.__setitem__(slice(1,3), [4,5])
6.4、常见陷阱
场景 | 说明 |
KeyError / IndexError | 自己决定抛什么异常;Python 规范期望 KeyError 用于 dict-like,IndexError 用于 list-like |
只读容器 | 不实现 setitem、delitem 即可 |
不可切片的键 | 在 getitem 里手动判断 isinstance(key, slice) |
6.5、总结
实现
__getitem__
/__setitem__
/__delitem__
就能把自定义对象变成「可索引、可赋值、可删除」的容器,语法与内置容器完全一致。7、__enter__
/__exit__
实现这两个方法后,实例就能与
with
语句配合,自动完成资源获取与清理,形成上下文管理器(Context Manager)。7.1、协议规则
方法 | 触发时机 | 返回值 | 异常处理 |
enter (self) | with 代码块开始时 | 任意对象(赋给 as 变量) | 无 |
exit (self, exc_type, exc_val, exc_tb) | with 代码块结束时(无论正常或异常) | True 吞掉异常,False 继续传播 | 接收异常三元组 |
7.2、最小可运行示例
运行输出
7.3、异常处理示例
7.4、总结
实现
__enter__
/__exit__
,你的对象就能被 with
托管,自动完成“资源获取—使用—释放”三步曲,异常也能优雅收尾。8、__add__
8.1、作用
当对象出现在
+
左侧时,解释器自动调用 obj.__add__(other)
,返回“加法”结果;若未实现,则尝试调用 other.__radd__(obj)
。8.2、签名
8.3、完整示例
8.4、类型检查
8.5、总结
重写
__add__
即可让自定义对象支持 +
运算符,实现“向量、金额、矩阵”等加法语义。9、__iter__
9.1、作用
使自定义对象可迭代(
for ... in obj:
、list(obj)
等)。解释器在需要迭代时,先调用
iter(obj)
→ obj.__iter__()
,该方法必须返回迭代器(即实现了 __next__
的对象)。9.2、最小实现
9.3、快捷写法:让对象自己就是迭代器
9.4、总结
只要类提供
__iter__
并返回一个带 __next__
的迭代器,就能让实例直接用于 for
循环、推导式、解包等所有迭代场景。📝 魔术方法(Magic / Special Methods)速查表
1、对象生命周期
方法 | 触发时机 | 典型用途 |
__new__(cls, ...) | 构建空实例 | 单例、不可变类型扩展 |
__init__(self, ...) | 填充初始状态 | 常规初始化 |
__del__(self) | 实例将被回收 | 释放非内存资源(文件、句柄) |
2、运算符重载
类别 | 方法 | 表达式 |
比较 | __eq__ , __ne__ , __lt__ , __le__ , __gt__ , __ge__ | a == b , a < b ... |
算术 | __add__ , __sub__ , __mul__ , __truediv__ , __floordiv__ , __mod__ , __pow__ | a + b , a - b ... |
位运算 | __and__ , __or__ , __xor__ , __lshift__ , __rshift__ | a & b , a \| b ... |
反向/就地 | __radd__ , __iadd__ ... | 1 + obj , obj += 1 |
一元 | __neg__ , __pos__ , __invert__ | -obj , ~obj 、 |
3、容器协议(列表/字典/集合行为)
方法 | 表达式/作用 | 备注 |
__len__ | len(obj) | 长度 |
__getitem__(self, key) | obj[key] 读 | 支持切片 slice |
__setitem__(self, key, val) | obj[key] = val | 写 |
__delitem__(self, key) | del obj[key] | 删 |
__contains__(self, item) | item in obj | 成员测试 |
__iter__(self) | for i in obj | 迭代器协议 |
__reversed__(self) | reversed(obj) | 反向迭代 |
4、可调用对象
方法 | 表达式 | 说明 |
__call__(self, *args, **kw) | obj(*args, **kw) | 让实例像函数一样使用 |
5、上下文管理器(with 语句)
方法 | 表达式 | 说明 |
__enter__(self) | with obj as x: | 进入上下文 |
__exit__(self, exc_type, exc_val, exc_tb) | 离开 with 块 | 清理资源、异常抑制 |
6、字符串/字节表示
方法 | 表达式 | 说明 |
__str__(self) | str(obj) / print(obj) | 用户友好字符串 |
__repr__(self) | repr(obj) / 调试台 | 开发者字符串,力求 eval(repr(obj))==obj |
__format__(self, fmt) | f"{obj:fmt}" | 自定义格式化 |
__bytes__(self) | bytes(obj) | 字节序列 |
7、描述符协议(@property 底层)
方法 | 触发 | 说明 |
__get__(self, obj, owner) | 读取属性 | 实现 property、缓存等 |
__set__(self, obj, val) | 设置属性 | 可写描述符 |
__delete__(self, obj) | 删除属性 | 可删除描述符 |
8、异步协议
方法 | 表达式 | 说明 |
__await__ | await obj | awaitable 对象 |
__aiter__ , __anext__ | async for | 异步迭代器 |
__aenter__ , __aexit__ | async with | 异步上下文 |
9、复制与序列化
方法 | 表达式 | 说明 |
__copy__ , __deepcopy__ | copy.copy(obj) / copy.deepcopy(obj) | 浅/深拷贝 |
__reduce__ , __reduce_ex__ | pickle.dumps(obj) | pickle 序列化钩子 |
10、抽象基类钩子(部分)
方法 | 说明 |
__subclasshook__ | issubclass 自定义判断 |
__instancecheck__ | isinstance 自定义判断 |