9、Python面向对象编程-类与对象
OOP-类与对象
本章学习知识点
- 类与对象:class 定义类、init 构造方法、self 实例引用、属性绑定特性、属性查找规则
面向对象编程(Object-Oriented Programming,简称 OOP)是一种以「对象」为核心的编程思想,它将数据(属性)和操作数据的方法封装在一起,通过封装、继承、多态三大核心特性实现代码复用和逻辑解耦,是 Python 开发中不可或缺的核心技术。
一、类与对象
类(Class)是对一类事物相似特征与行为的抽象总结(如“教师类”包含姓名、授课技能等共性),而对象是类的具体实例(如“张老师”“李老师”)。类比现实世界:类是“汽车设计图纸”,对象是“根据图纸造出的具体汽车”——类是模板,对象是模板创建的实体。
1.1、类的定义与核心规则
类是对象的抽象模板,其定义和使用遵循明确的核心规则,是理解 OOP 的基础:
- 类的定义方式:通过
class关键字定义,语法为class 类名:,类体包含属性(数据)和方法(函数); - 核心执行特性
- 定义顺序:必须先定义类,后创建对象(与函数 “先定义后调用” 逻辑一致);
- 执行时机:类体代码在「类定义阶段」立即执行,生成类的名称空间(存放类的所有属性和方法);
- 调用差异:调用函数返回执行结果,调用类(实例化)返回对象本身;
- 关键操作
类.__dict__:查看类的名称空间(包含所有类属性和方法的键值对);- 点语法(.):Python 专属的属性访问方式,如
类.属性或对象.属性。
1.2 self 与 init(构造 / 初始化方法)
self 和 __init__ 是类实例化和属性初始化的核心,两者配合完成实例的 “初始状态构建”。
1.2.1、核心概念
-
self:实例的自身引用
self是 Python 类中实例方法的必选第一个参数(约定俗成的命名,非关键字),核心作用是:- 指代当前调用方法的实例本身(谁调用方法,self 就指向谁);
- 通过 self 访问 / 修改实例的专属属性、调用实例的其他方法;
- 调用实例方法时,Python 会自动把实例传给 self,无需手动传参。
-
init:实例的初始化方法
__init__是 Python 类的特殊内置方法(也叫构造方法),核心作用是:- 创建实例时自动触发执行,无需手动调用;
- 用于给实例绑定初始属性,完成实例的初始化;
- 必须以 self 为第一个参数,支持自定义参数,且不能返回非 None 的值。
-
两者关系
__init__是 “给实例装初始属性” 的流程,self是这个流程中 “被装修的那个实例”- 通过
self在__init__中绑定的属性,会成为每个实例的专属数据。
1.2.2、示例
-
示例
class Student: # 类属性:所有实例共享(比如学校名称) school = "北京大学" # __init__:初始化方法,创建实例时自动执行 # self:指向当前创建的Student实例(必选第一个参数) # name/age/score:自定义参数,用于给实例绑定专属属性 def __init__(self, name, age, score): # 给当前实例绑定专属属性(self.属性名 = 值) self.name = name # 姓名:每个学生独有 self.age = age # 年龄:每个学生独有 self.score = score # 分数:每个学生独有 # 初始化时通过self调用实例方法(校验分数合法性) self.check_score() # 实例方法:检查分数是否合法(依赖self访问实例属性) def check_score(self): if not 0 <= self.score <= 100: raise ValueError(f"{self.name}的分数{self.score}不合法!必须在0-100之间") # 实例方法:打印学生信息(通过self访问实例属性/类属性) def show_info(self): print(f"【学校】:{self.school}(类属性,所有学生共享)") print(f"【姓名】:{self.name},【年龄】:{self.age},【分数】:{self.score}(实例属性,专属)") # ---------------------- 测试实例 ---------------------- # 1. 创建第一个学生实例:自动调用__init__,Python自动把s1传给self s1 = Student("张三", 18, 95) # 调用实例方法:self指向s1,访问s1的属性 s1.show_info() # 输出:【学校】:北京大学...【姓名】:张三... print("-" * 50) # 2. 创建第二个学生实例:self此时指向s2,绑定s2的专属属性 s2 = Student("李四", 19, 88) s2.show_info() # 输出:【学校】:北京大学...【姓名】:李四... # 3. 错误示例:分数不合法(__init__中调用check_score触发报错) # s3 = Student("王五", 20, 105) # 报错:王五的分数105不合法!必须在0-100之间 # 查看实例的名称空间(独有属性) print(s1.__dict__) # 输出:{'name': '张三', 'age': 18, 'score': 95} -
常见误区
- 忘记 self 参数:实例方法缺少 self 会导致调用时参数不匹配报错;
- 属性绑错位置:
__init__中直接写name = name(少 self),会变成局部变量,实例无法访问; - 手动调用
__init__:无需手动执行s1.__init__(),重复调用可能覆盖已有属性; - self 不是关键字:可以用 this 代替,但违反 Python 规范,降低可读性;
__init__有返回值:__init__只能返回 None,返回其他值会报错。
-
总结
- self 是实例的 “自身引用”,区分不同实例的专属属性 / 方法,谁调用就指向谁;
__init__是实例的 “初始化入口”,创建实例时自动执行,通过 self 完成属性初始化;- 核心逻辑:创建实例 → Python 自动传实例给 self →
__init__执行 → 实例属性初始化完成。
-
一句话记住:
__init__负责给实例 “赋初始值”,self负责定位 “该给哪个实例赋值”,两者配合让每个实例都有自己的初始状态。
1.3、属性查找规则与绑定特性
- 说明
- “属性查找规则” 是指访问实例 / 类的属性时,解释器从哪里找这个属性;
- “绑定特性” 是指属性(含方法)如何关联到实例或类(归谁所有)。
1.3.1、查找规则
当通过 实例.属性 或 类.属性 访问属性时,Python 解释器会按固定顺序搜索,核心口诀:实例找属性,先找自己的,再找类的,最后找父类的;类找属性,只找自己的和父类的。
-
实例访问属性的搜索顺序
-
顺序说明:
实例.__dict__(自身实例属性) →类.__dict__(类属性) → 父类.dict(父类属性)→ … → 顶级父类,找到即停止;若全找不到则报AttributeError。 -
示例
# 定义父类 class Base: default = 0 # 父类属性 # 定义子类(继承Base) class Person(Base): species = "人类" # 子类类属性 def __init__(self, name): self.name = name # 实例属性 # 创建实例 p = Person("张三") # 第一步:找实例属性(p.__dict__)→ 找到name,返回“张三” print(p.name) # 第二步:实例无species,找类属性(Person.__dict__)→ 返回“人类” print(p.species) # 第三步:实例/类无default,找父类属性(Base.__dict__)→ 返回0 print(p.default) # 第四步:实例/类/父类均无age → 报错 # print(p.age) # AttributeError: 'Person' object has no attribute 'age'
-
-
类访问属性的搜索顺序
- 顺序说明:
类.__dict__(自身类属性) → 父类.dict(父类属性)→ … → 顶级父类,类永远无法访问实例属性(实例属性属于具体实例,不属于类)。
- 顺序说明:
-
查找规则的关键细节
- 实例属性遮蔽类属性:若实例有和类同名的属性,实例访问时会优先返回自身属性(类属性被 “隐藏”);
- 动态绑定属性也遵循规则:Python 支持运行时给类 / 实例新增属性,新增的属性同样按上述顺序查找。
-
示例代码(动态绑定 + 遮蔽特性)
class Cat: pass Cat.color = "黑色" # 动态给类绑定属性(类.__dict__新增color) c = Cat() c.name = "小黑" # 动态给实例绑定属性(实例.__dict__新增name) c.color = "白色" # 动态给实例绑定与类同名的属性(遮蔽类属性) print(Cat.color) # 类找属性:Cat.__dict__ → 黑色 print(c.name) # 实例找属性:c.__dict__ → 小黑 print(c.color) # 实例找属性:c.__dict__有 → 白色(遮蔽类属性)
1.3.2、方法的绑定特性
属性的绑定本质是 “归属关系”—— 确定属性是属于类(所有实例共享),还是属于某个具体实例(仅该实例独有),核心分为普通属性绑定和方法绑定两类:
-
普通属性的绑定(类属性 vs 实例属性)
类型 定义方式 绑定对象 共享性 修改方式 核心特点 类属性 类体中( __init__外)直接赋值类本身 所有实例共享 仅通过 类名.属性修改改则全改,实例仅能读取(除非遮蔽) 实例属性 __init__/ 实例方法中self.属性具体实例 每个实例独有 实例名.属性或self.属性修改-
示例
class Student: # 类属性:绑定到Student类,所有实例共享 school = "北京大学" def __init__(self, name): # 实例属性:绑定到当前实例(self),每个实例独有 self.name = name # 1. 类属性:类和所有实例都能访问(共享) print(Student.school) # 输出:北京大学 s1 = Student("张三") s2 = Student("李四") print(s1.school, s2.school) # 输出:北京大学 北京大学 # 2. 实例属性:仅所属实例能访问,类无法访问 print(s1.name) # 输出:张三 # print(Student.name) # 报错:AttributeError(类无name属性) # 3. 实例“修改”类属性:实际是创建同名实例属性(遮蔽类属性) s1.school = "清华大学" # 给s1新增实例属性school,非修改类属性 print(s1.school) # 输出:清华大学(优先读实例属性) print(s2.school) # 输出:北京大学(类属性未变) print(Student.school) # 输出:北京大学(类属性未变) # 4. 真正修改类属性:必须通过类名修改(所有实例共享变化) Student.school = "复旦大学" print(s2.school) # 输出:复旦大学(s2无同名实例属性,读类属性) print(s1.school) # 输出:清华大学(s1有同名实例属性,仍读自己的)
-
-
方法的绑定特性(可调用的特殊属性)
方法本质是 “可调用的属性”,绑定规则与普通属性不同,核心分为三类:
方法类型 定义方式 绑定对象 调用方式 核心参数 适用场景 实例方法 def 方法名(self, ...):实例 实例.方法名()self 操作实例属性(如读取 / 修改 name) 类方法 @classmethod + def 方法名(cls, ...):类 类名.方法名()/实例.方法名()cls 操作类属性(如修改 school) 静态方法 @staticmethod + def 方法名(...):无绑定 类名.方法名()/实例.方法名()无 通用工具函数(与类 / 实例无关) -
示例
class Dog: type = "犬科" # 类属性 # 实例方法:绑定到实例,依赖self(操作实例属性) def __init__(self, name): self.name = name # 实例属性 def bark(self): print(f"{self.name}:汪汪叫") # 依赖实例属性name # 类方法:绑定到类,依赖cls(操作类属性) @classmethod def change_type(cls, new_type): cls.type = new_type # 修改类属性type # 静态方法:无绑定,仅逻辑归属类 @staticmethod def run(): print("狗狗在跑") # 不依赖任何类/实例属性 # 1. 实例方法:绑定实例,self自动指向调用实例(d1) d1 = Dog("旺财") d1.bark() # 输出:旺财:汪汪叫 # 2. 类方法:绑定类,cls自动指向Dog类(实例调用也指向类) Dog.change_type("哺乳动物") print(Dog.type) # 输出:哺乳动物 d1.change_type("食肉动物") # 实例调用类方法,cls仍指向Dog print(Dog.type) # 输出:食肉动物 # 3. 静态方法:无绑定,类/实例调用效果完全一致 Dog.run() # 输出:狗狗在跑 d1.run() # 输出:狗狗在跑
-
-
核心总结
- 查找规则:实例找属性 “先自身→类→父类”,类找属性 “仅类→父类”;实例属性会遮蔽同名类属性,动态绑定的属性也遵循此规则;
- 绑定特性:类属性共享、实例属性独有;实例方法绑定实例(依赖 self)、类方法绑定类(依赖 cls)、静态方法无绑定;
- 关键注意:修改类属性必须通过
类名.属性,实例 “修改” 类属性实际是新增实例属性;类永远无法访问实例属性。
更多推荐



所有评论(0)