本章摘要
- 核心目标:掌握用
class定义类、创建对象,能用面向对象思维组织代码 - 核心知识点:
class定义类、__init__初始化方法、self参数、实例化、实例属性、实例方法、类属性与实例属性区别 - 实操产出:学生信息管理类,含自我介绍、及格判断、加分功能
- 前置基础:第14章模块与导入、第7章字典、第4-5章函数与循环
一、写在前面
上一章你学了模块------把代码拆分到不同文件,方便管理和复用。但到目前为止,你写的所有代码都是面向过程的:一步一步告诉计算机"先做这个,再做那个",用函数把重复逻辑封装起来。
这种写法没问题,但当程序变大、要描述的东西变复杂时,你会发现自己一直在跟一堆散乱的变量和函数打交道。比如管理学生信息,你需要一个变量存姓名、一个存年龄、一个存成绩,再写几个函数来操作这些变量......数据和行为是分开的,容易乱。
**面向对象编程Object-Oriented Programming(OOP)**的思路完全不同:把数据和行为打包到一起,形成一个"对象"。你在 Scratch 里其实早就用过这个思路------每个角色有自己的属性(位置、大小、造型)和脚本(代码积木),角色本身就是"对象"的雏形。这一章,我们就从 Scratch 角色出发,正式走进 Python 的类与对象。
二、什么是类和对象
结论:类是模板(图纸),对象是按模板造出来的具体实例。
先说一个生活类比。你见过饼干模具吗?一个星星形状的模具,可以压出无数块星星饼干。模具本身不是饼干,但它决定了每一块饼干长什么样。在这里:
- 模具 = 类(
class):定义了"有什么属性"和"能做什么动作" - 饼干 = 对象(
object):按模具造出来的具体实例,每个对象都有自己的属性值
再比如汽车图纸和具体的车:图纸规定了"有颜色、有品牌、有速度",但具体每辆车的颜色、品牌、速度各不相同。图纸是类,每辆车是对象。
2.1 用 class 定义类
Python 用 class 关键字定义类,语法非常简洁:
python
# 定义一个最简单的类
class Dog:
pass # pass 是占位符,表示"什么都不做"
print(Dog) # <class '__main__.Dog'>
class Dog: 声明了一个名为 Dog 的类,pass 表示类体暂时为空。打印 Dog 可以看到它是一个类(class)。
2.2 实例化对象
有了类,就可以"造"对象了------这叫实例化 ,语法是 类名():
python
class Dog:
pass
# 实例化:用 Dog 类创建两个对象
dog1 = Dog()
dog2 = Dog()
print(dog1) # <__main__.Dog object at 0x...>
print(dog2) # <__main__.Dog object at 0x...>
print(dog1 == dog2) # False ------ 两个不同的对象
dog1 和 dog2 都是 Dog 类的实例,但它们是两个独立的对象(内存地址不同)。就像用同一个模具压出两块饼干,虽然形状一样,但它们是两块独立的饼干。
类名习惯用大驼峰命名法 :每个单词首字母大写,如
Student、MyDog、PhoneNumber。这是 Python 社区的约定俗成。
三、__init__ 初始化方法
结论:__init__ 是对象创建时自动执行的方法,用来给对象设置初始属性。
类只定义了"有什么",但具体每个对象的属性值是多少?这就需要 __init__ 方法了。
3.1 基本语法
__init__(init 是 initialize 的缩写)是 Python 的特殊方法,名字两端各有两个下划线。每次实例化对象时,Python 会自动调用它:
python
class Dog:
def __init__(self, name, age):
self.name = name # 给对象设置 name 属性
self.age = age # 给对象设置 age 属性
# 实例化时传入参数,__init__ 自动接收
dog1 = Dog("旺财", 3)
dog2 = Dog("小黑", 5)
print(dog1.name) # 旺财
print(dog1.age) # 3
print(dog2.name) # 小黑
print(dog2.age) # 5
注意看:实例化时写的是 Dog("旺财", 3),传了两个参数,但 __init__ 的参数列表里有三个(self, name, age)。self 不需要你手动传,Python 自动帮你填------它就是正在创建的那个对象。
3.2 __init__ 执行时机
python
class Cat:
def __init__(self, name):
self.name = name
print(f"{self.name} 出生了!")
# 创建对象的瞬间,__init__ 自动执行
cat = Cat("咪咪")
# 输出:咪咪 出生了!
你不需要手动调用
__init__,它在类名()的那一刻自动执行。就像 Scratch 里角色被添加到舞台时,"当绿旗被点击"积木自动执行一样。
四、self 参数详解
结论:self 就是"当前这个对象自己",用 self.属性名 访问对象的属性,用 self.方法名() 调用对象的方法。
self 是面向对象里最容易让人困惑的概念,但其实你可以这样理解:当你叫"旺财"坐下时,"旺财"知道你在叫它,它自己执行"坐下"这个动作。self 就是那个"自己"。
4.1 self 指向当前对象
python
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
# 通过 self.name 访问当前对象的 name 属性
print(f"{self.name} 汪汪汪!")
dog1 = Dog("旺财")
dog2 = Dog("小黑")
dog1.bark() # 旺财 汪汪汪!
dog2.bark() # 小黑 汪汪汪!
当 dog1.bark() 执行时,self 就是 dog1,所以 self.name 是 "旺财"。
当 dog2.bark() 执行时,self 就是 dog2,所以 self.name 是 "小黑"。
4.2 self 不能省略
python
class Dog:
def __init__(self, name):
self.name = name
# ❌ 错误写法:漏掉 self
# def bark():
# print(f"{name} 汪汪汪!") # NameError: name 'name' is not defined
在实例方法里,
self必须是第一个参数。虽然调用时你不用手动传,但定义时一定要写。Python 靠self知道"这个方法要操作哪个对象"。
4.3 用 self 在方法间共享属性
python
class Dog:
def __init__(self, name):
self.name = name
self.energy = 100 # 初始精力值 100
def run(self):
self.energy -= 20 # 跑一步消耗 20 精力
print(f"{self.name} 跑了,剩余精力 {self.energy}")
def sleep(self):
self.energy = 100 # 睡一觉恢复精力
print(f"{self.name} 睡醒了,精力恢复到 {self.energy}")
dog = Dog("旺财")
dog.run() # 旺财 跑了,剩余精力 80
dog.run() # 旺财 跑了,剩余精力 60
dog.sleep() # 旺财 睡醒了,精力恢复到 100
self.energy 在 __init__ 中创建,在 run() 和 sleep() 中通过 self 访问和修改。self 让同一个对象的不同方法共享同一份数据。
五、实例属性
结论:实例属性是每个对象各自拥有的数据,在 __init__ 中通过 self.属性名 = 值 创建。
5.1 定义和访问实例属性
python
class Student:
def __init__(self, name, age, score):
self.name = name # 姓名
self.age = age # 年龄
self.score = score # 成绩
s1 = Student("小明", 18, 85)
s2 = Student("小红", 19, 92)
# 每个对象有自己的属性值
print(s1.name, s1.age, s1.score) # 小明 18 85
print(s2.name, s2.age, s2.score) # 小红 19 92
5.2 动态添加实例属性
Python 允许在创建对象后动态添加属性(其他语言一般不行):
python
s1 = Student("小明", 18, 85)
s1.class_name = "高三一班" # 动态添加,只有 s1 有这个属性
print(s1.class_name) # 高三一班
# print(s2.class_name) # ❌ AttributeError: 'Student' object has no attribute 'class_name'
虽然 Python 支持动态添加属性,但不推荐 这么做。好的习惯是在
__init__里把所有属性都定义好,让代码清晰可读。
5.3 修改实例属性
python
s1 = Student("小明", 18, 85)
print(s1.score) # 85
s1.score = 90 # 直接赋值修改
print(s1.score) # 90
s1.age += 1 # 也可以用运算后赋值
print(s1.age) # 19
六、实例方法
结论:实例方法是定义在类里的函数,第一个参数永远是 self,通过 对象.方法名() 调用。
6.1 定义和调用
python
class Student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
# 实例方法:自我介绍
def introduce(self):
print(f"大家好,我叫{self.name},今年{self.age}岁,成绩{self.score}分")
# 创建对象并调用方法
s1 = Student("小明", 18, 85)
s1.introduce() # 大家好,我叫小明,今年18岁,成绩85分
实例方法和普通函数几乎一样,区别只有两点:
- 定义在类的内部
- 第一个参数是
self(调用时不用手动传)
6.2 方法中调用其他方法
python
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def is_pass(self):
return self.score >= 60
def introduce(self):
status = "及格" if self.is_pass() else "不及格"
print(f"{self.name} 成绩{self.score}分,{status}")
s = Student("小明", 85)
s.introduce() # 小明 成绩85分,及格
在
introduce()中通过self.is_pass()调用了同一个类的另一个方法。这是面向对象的常见写法------方法之间通过self互相协作。
6.3 带参数的实例方法
方法除了 self,还可以有额外参数:
python
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def add_score(self, points):
self.score += points
print(f"{self.name} 加了{points}分,现在成绩{self.score}分")
s = Student("小明", 85)
s.add_score(10) # 小明 加了10分,现在成绩95分
s.add_score(5) # 小明 加了5分,现在成绩100分
七、类属性与实例属性的区别
结论:类属性属于类本身,所有对象共享;实例属性属于每个对象,各自独立。
这是本章最容易混淆的知识点,我们慢慢来。
7.1 什么是类属性
类属性直接写在类里(不在任何方法里),属于类本身:
python
class Student:
# 类属性:写在方法外面
school = "第一中学"
def __init__(self, name, score):
# 实例属性:写在 __init__ 里
self.name = name
self.score = score
s1 = Student("小明", 85)
s2 = Student("小红", 92)
# 类属性通过类名访问
print(Student.school) # 第一中学
# 类属性也可以通过对象访问
print(s1.school) # 第一中学
print(s2.school) # 第一中学
7.2 类属性 vs 实例属性
| 对比项 | 类属性 | 实例属性 |
|---|---|---|
| 定义位置 | 类体中,方法外面 | __init__ 方法中,self.属性名 |
| 归属 | 属于类 | 属于每个对象 |
| 共享性 | 所有对象共享同一份 | 每个对象各自一份 |
| 访问方式 | 类名.属性 或 对象.属性 |
对象.属性 |
| 修改方式 | 类名.属性 = 新值 |
对象.属性 = 新值 |
| 典型用途 | 所有对象共用的常量/计数器 | 每个对象独有的数据 |
7.3 一个容易踩的坑
python
class Student:
school = "第一中学"
def __init__(self, name):
self.name = name
s1 = Student("小明")
s2 = Student("小红")
# 通过类名修改类属性 → 所有对象都变
Student.school = "第二中学"
print(s1.school) # 第二中学
print(s2.school) # 第二中学
# 通过对象"修改"类属性 → 其实是给对象添加了实例属性
s1.school = "第三中学"
print(s1.school) # 第三中学(s1 的实例属性遮蔽了类属性)
print(s2.school) # 第二中学(s2 仍读类属性)
print(Student.school) # 第二中学(类属性本身没变)
⚠️ 通过对象修改类属性时,Python 不会改类属性,而是给这个对象创建一个同名的实例属性。这叫"遮蔽"。修改类属性一律用
类名.属性 = 新值,避免混淆。
7.4 类属性的典型用途:对象计数器
python
class Student:
count = 0 # 类属性:统计创建了多少个学生
def __init__(self, name):
self.name = name
Student.count += 1 # 每创建一个对象,计数器 +1
print(f"{self.name} 入学!当前学生总数:{Student.count}")
s1 = Student("小明") # 小明 入学!当前学生总数:1
s2 = Student("小红") # 小红 入学!当前学生总数:2
s3 = Student("小刚") # 小刚 入学!当前学生总数:3
print(f"一共创建了 {Student.count} 个学生对象") # 一共创建了 3 个学生对象
类属性
count被所有对象共享,每次__init__执行时Student.count += 1,就能统计总人数。这是类属性最常见的应用场景。
八、Scratch 积木对照
| Scratch | Python | 说明 |
|---|---|---|
| 角色(小猫) | 对象(实例) | Scratch 拖一个角色到舞台 = Python 实例化一个对象 |
| 角色属性(x坐标/大小/造型) | 实例属性 self.xxx |
每个角色有自己的属性值 = 每个对象有自己的实例属性 |
| 角色脚本(积木堆) | 实例方法 def xxx(self) |
角色的脚本让角色做事 = 对象的方法让对象做事 |

python
# Scratch 角色 → Python 对象
# Scratch: 拖一只小猫到舞台,设置 x=100, y=50,脚本:当绿旗点击 → 说"你好"
# Python:
class Cat:
def __init__(self, x, y):
self.x = x # 相当于角色的 x 坐标
self.y = y # 相当于角色的 y 坐标
def say_hello(self): # 相当于角色的"说"积木
print(f"小猫在({self.x}, {self.y})说:你好!")
cat = Cat(100, 50) # 创建小猫对象(相当于拖角色到舞台)
cat.say_hello() # 小猫在(100, 50)说:你好!
在 Scratch 里,你先选一个角色,给它设属性、写脚本,然后把它拖到舞台上就有了一只活的角色。Python 面向对象也是这个流程:先写 class 定义"角色模板",再实例化创建具体的"角色",每个角色有自己的属性和可以执行的动作。
九、综合实战:学生信息管理类
综合运用类定义、__init__、self、实例方法、属性操作,写一个学生信息管理类:
python
# ========================================
# 学生信息管理类
# ========================================
class Student:
# 类属性:学校名称(所有学生共享)
school = "第一中学"
def __init__(self, name, age, score):
"""初始化学生信息"""
self.name = name # 姓名
self.age = age # 年龄
self.score = score # 成绩
def introduce(self):
"""自我介绍"""
print(f"大家好,我是{self.school}的{self.name},今年{self.age}岁,成绩{self.score}分")
def is_pass(self):
"""判断是否及格(60分及格)"""
if self.score >= 60:
print(f"{self.name} 成绩{self.score}分,及格了 ✅")
return True
else:
print(f"{self.name} 成绩{self.score}分,不及格 ❌")
return False
def add_score(self, points):
"""加分:给成绩加上指定分数"""
self.score += points
print(f"{self.name} 加了{points}分,现在成绩{self.score}分")
# ====== 创建学生对象 ======
print("=" * 40)
s1 = Student("小明", 18, 55)
s2 = Student("小红", 17, 88)
# 自我介绍
s1.introduce()
s2.introduce()
print("-" * 40)
# 判断及格
s1.is_pass()
s2.is_pass()
print("-" * 40)
# 小明补习加分,再判断
s1.add_score(15) # 加15分
s1.is_pass() # 重新判断
print("-" * 40)
# 查看学校(类属性)
print(f"{s1.name} 和 {s2.name} 都在 {Student.school}")
运行结果:
========================================
大家好,我是第一中学的小明,今年18岁,成绩55分
大家好,我是第一中学的小红,今年17岁,成绩88分
----------------------------------------
小明 成绩55分,不及格 ❌
小红 成绩88分,及格了 ✅
----------------------------------------
小明 加了15分,现在成绩70分
小明 成绩70分,及格了 ✅
----------------------------------------
小明 和 小红 都在 第一中学
分步拆解:
- 类属性
school:所有学生共享同一个学校名称,通过self.school或Student.school访问 __init__初始化:创建对象时自动执行,把传入的姓名、年龄、成绩存为实例属性introduce()方法 :通过self.name、self.age、self.score拼接自我介绍,还访问了类属性self.schoolis_pass()方法 :判断成绩是否大于等于 60,返回True/False,同时打印结果add_score()方法 :修改实例属性self.score,展示了方法可以改变对象状态- 方法间协作 :
add_score()改了分数后,再调is_pass()就能得到新结果------对象的状态是持续保存的
十、常见报错速查表
| 报错信息 | 出现场景 | 原因 | 解决方法 |
|---|---|---|---|
TypeError: __init__() missing N required positional argument |
实例化对象 | 创建对象时少传了参数 | 检查 __init__ 需要几个参数(不含 self),实例化时全部传入 |
AttributeError: 'Dog' object has no attribute 'xxx' |
访问属性 | 对象没有这个属性,或 __init__ 中忘记定义 |
在 __init__ 中添加 self.xxx = 值 |
TypeError: bark() takes 0 positional arguments but 1 was given |
调用方法 | 定义方法时漏写了 self 参数 |
方法第一个参数必须是 self |
NameError: name 'self' is not defined |
类外部使用 self | 在类外面或类方法外用了 self |
self 只能在类的方法内部使用 |
TypeError: 'type' object is not subscriptable |
实例化语法错误 | 把 Dog() 写成了 Dog[] |
实例化用小括号 类名() |
十一、本章小结
本章你正式迈入面向对象编程的大门:
- 类与对象 ------类是模板(模具),对象是按模板创建的具体实例,用
class定义类,用类名()实例化对象 __init__初始化 ------对象创建时自动执行,通过self.属性 = 值设置初始属性self参数 ------就是"当前对象自己",方法内通过self访问属性和调用其他方法- 实例属性 ------每个对象各自拥有的数据,在
__init__中定义 - 实例方法 ------定义在类里的函数,第一个参数是
self,通过对象.方法名()调用 - 类属性 vs 实例属性 ------类属性所有对象共享,实例属性各自独立;修改类属性用
类名.属性 = 新值
面向对象是 Python 编程的分水岭。别着急,多写几遍类,你会发现它就是把"数据"和"操作数据的函数"打包在一起,逻辑更清晰。
往期回顾:
零基础学 Python 第1章 | 全平台装环境 + VS Code 配置 + 第一个 Hello World
零基础学 Python 第2章 | print 输出 + 变量赋值 + input 交互输入
零基础学 Python 第3章 | 条件判断:if / elif / else
零基础学 Python 第 4 章 | for 循环与 range():让程序帮你重复干活
零基础学 Python 第 5 章 | while 循环与循环嵌套:让程序看着条件自己干活
零基础学 Python 第 6 章 | 元组与集合:不可变数据与自动去重
零基础学 Python 第7章 | 字典 dict:键值对存储
零基础学 Python 第9章 | 函数基础:def、参数与返回值
零基础学 Python 第10章 | 函数进阶:参数灵活用法与作用域
零基础学 Python 第12章 | 文件读写:让数据持久保存
零基础学 Python 第13章 | 异常处理:try-except 让程序不崩溃
下一章预告 :你已经会定义类和创建对象了,但如果每次都要从头写一个全新的类,太累了吧?下一章学习继承与方法重写------让子类直接复用父类的属性和方法,再按需修改,代码复用效率翻倍。
文末资源区
| 资源 | 说明 |
|---|---|
| Python 官方文档 - 类与对象 | https://docs.python.org/3/tutorial/classes.html |
| Python 官方文档 - 类的定义 | https://docs.python.org/3/reference/datamodel.html#classes |
| 本章完整源码 | chapter15_all.py(关注梅雅达编程笔记,评论领资料) |
|
每节课都配套课后练习题与完整源码。代码报错、看不懂知识点,可在评论区粘贴问题,逐一解答!
欢迎关注梅雅达编程笔记,持续分享Python零基础学习内容

