零基础学 Python 第15章 | 类与对象:面向对象编程入门

本章摘要

  1. 核心目标:掌握用 class 定义类、创建对象,能用面向对象思维组织代码
  2. 核心知识点:class 定义类、__init__ 初始化方法、self 参数、实例化、实例属性、实例方法、类属性与实例属性区别
  3. 实操产出:学生信息管理类,含自我介绍、及格判断、加分功能
  4. 前置基础:第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 ------ 两个不同的对象

dog1dog2 都是 Dog 类的实例,但它们是两个独立的对象(内存地址不同)。就像用同一个模具压出两块饼干,虽然形状一样,但它们是两块独立的饼干。

类名习惯用大驼峰命名法 :每个单词首字母大写,如 StudentMyDogPhoneNumber。这是 Python 社区的约定俗成。


三、__init__ 初始化方法

结论:__init__ 是对象创建时自动执行的方法,用来给对象设置初始属性。

类只定义了"有什么",但具体每个对象的属性值是多少?这就需要 __init__ 方法了。

3.1 基本语法

__init__initinitialize 的缩写)是 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分

实例方法和普通函数几乎一样,区别只有两点:

  1. 定义在类的内部
  2. 第一个参数是 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分,及格了 ✅
----------------------------------------
小明 和 小红 都在 第一中学

分步拆解:

  1. 类属性 school :所有学生共享同一个学校名称,通过 self.schoolStudent.school 访问
  2. __init__ 初始化:创建对象时自动执行,把传入的姓名、年龄、成绩存为实例属性
  3. introduce() 方法 :通过 self.nameself.ageself.score 拼接自我介绍,还访问了类属性 self.school
  4. is_pass() 方法 :判断成绩是否大于等于 60,返回 True/False,同时打印结果
  5. add_score() 方法 :修改实例属性 self.score,展示了方法可以改变对象状态
  6. 方法间协作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[] 实例化用小括号 类名()

十一、本章小结

本章你正式迈入面向对象编程的大门:

  1. 类与对象 ------类是模板(模具),对象是按模板创建的具体实例,用 class 定义类,用 类名() 实例化对象
  2. __init__ 初始化 ------对象创建时自动执行,通过 self.属性 = 值 设置初始属性
  3. self 参数 ------就是"当前对象自己",方法内通过 self 访问属性和调用其他方法
  4. 实例属性 ------每个对象各自拥有的数据,在 __init__ 中定义
  5. 实例方法 ------定义在类里的函数,第一个参数是 self,通过 对象.方法名() 调用
  6. 类属性 vs 实例属性 ------类属性所有对象共享,实例属性各自独立;修改类属性用 类名.属性 = 新值

面向对象是 Python 编程的分水岭。别着急,多写几遍类,你会发现它就是把"数据"和"操作数据的函数"打包在一起,逻辑更清晰。

往期回顾:

《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 第8章 | 字符串操作与格式化输出

零基础学 Python 第9章 | 函数基础:def、参数与返回值

零基础学 Python 第10章 | 函数进阶:参数灵活用法与作用域

零基础学 Python 第11章 | 匿名函数与高阶函数

零基础学 Python 第12章 | 文件读写:让数据持久保存

零基础学 Python 第13章 | 异常处理:try-except 让程序不崩溃

零基础学 Python 第14章 | 模块、包与第三方库

下一章预告 :你已经会定义类和创建对象了,但如果每次都要从头写一个全新的类,太累了吧?下一章学习继承与方法重写------让子类直接复用父类的属性和方法,再按需修改,代码复用效率翻倍。


文末资源区

资源 说明
Python 官方文档 - 类与对象 https://docs.python.org/3/tutorial/classes.html
Python 官方文档 - 类的定义 https://docs.python.org/3/reference/datamodel.html#classes
本章完整源码 chapter15_all.py(关注梅雅达编程笔记,评论领资料)

|


每节课都配套课后练习题与完整源码。代码报错、看不懂知识点,可在评论区粘贴问题,逐一解答!

欢迎关注梅雅达编程笔记,持续分享Python零基础学习内容

相关推荐
我星期八休息16 小时前
网络编程—应用层HTTP协议
linux·运维·开发语言·前端·网络·网络协议·http
梅雅达编程笔记16 小时前
零基础学 Python 第14章 | 模块、包与第三方库
开发语言·python·django·numpy·pandas
Mr__Miss16 小时前
Java泛型完全指南:从入门到精通
java·开发语言·python
赵庆明老师17 小时前
Vben精讲:14-Vben远程加载语言包
开发语言·vben
嘿丨嘿17 小时前
VLA 入门(六):VLA 如何进行强化学习后训练?
人工智能·python·深度学习·机器人
hehelm18 小时前
AI大模型接入SDK—通用模块设计
linux·开发语言·c++
祉猷并茂,雯华若锦18 小时前
Appium 3.x实战:获取元素属性全解析
python·appium·自动化
宠友信息18 小时前
消息序号如何保证即时通讯源码聊天记录稳定加载
java·spring boot·redis·python·mysql·uni-app
小柯南敲键盘19 小时前
批量图片翻译与视频字幕一站式解决高效跨境电商沟通难题
大数据·人工智能·python·音视频