/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
Python进阶第一课:从类与对象到self和属性访问
在 Python 基础阶段,我们已经学习过变量、条件判断、循环和函数。这些工具可以帮助我们完成具体步骤。进入面向对象编程后,还需要学习另一件事:怎样把相互关联的数据与操作组织在一起,用对象承担程序中的不同职责。
例如,描述一辆汽车,需要记录颜色、轮胎数等信息,也需要表示“跑”这样的行为;描述一部手机,则需要组织开机、拍照、关机等操作。面向对象编程为这些需求提供了统一的组织方式。
本文按照课程随堂笔记的顺序,依次学习:面向对象和面向过程 → 面向对象特征介绍 → 入门案例_汽车类 → self关键字介绍 → 入门案例_手机类 → 类外_获取和设置对象的属性 → 类内_获取对象的属性。
阅读时重点理解三个问题:类与对象是什么关系,实例方法中的 self 代表谁,以及对象的属性应该怎样设置和读取。实操使用六个课堂练习,每个实操模块末尾附对应的完整原代码,便于分别保存运行。示例仅需 Python 3,无须安装第三方库。
一、面向对象和面向过程
1.1 什么是编程思想
编程思想,就是利用计算机解决问题时组织思路的方式。
同一个需求,既可以从“先做什么、再做什么”出发,也可以从“有哪些对象、分别负责什么”出发。前者侧重处理步骤,后者侧重对象及其职责。
1.2 面向过程:以步骤为基础完成操作
面向过程是一种编程思想,强调按照步骤,也就是过程,完成各种操作。
以洗衣服为例,可以把任务分解为:准备衣物、加入洗涤剂、清洗、漂洗、脱水。编写程序时,就围绕这些步骤安排函数和调用顺序。
这时,首先需要回答的问题是:完成这件事需要哪些步骤,步骤之间怎样衔接?
1.3 面向对象:以对象为基础组织操作
面向对象是一种编程思想,强调以对象为基础完成各种操作。
仍以洗衣服为例,我们可以先关注“洗衣机”这个对象:它具有容量、运行状态等信息,也提供洗涤、漂洗、脱水等功能。使用者通过它提供的操作发出指令,具体步骤由相应功能负责完成。
这时,首先需要回答的问题是:这个场景中有哪些对象,每个对象保存什么信息、能够完成什么操作?
课件将面向对象的三大思想特点概括为:
面向对象并没有取消步骤。对象的方法内部,仍然需要条件判断、循环、函数调用等过程来完成工作。可以先这样理解课件中“面向对象是基于面向过程的”这句话:用对象组织职责,用具体步骤实现职责。
| 主要关注点 | 完成任务的步骤 | 参与任务的对象及其职责 |
| 组织思路 | 分解步骤,安排执行顺序 | 抽象对象,组织属性与行为 |
| 洗衣服的例子 | 清洗、漂洗、脱水依次执行 | 调用洗衣机对象提供的功能 |
“万物皆对象”可以作为理解面向对象的入门提示:尝试从事物的特征与行为出发建立模型。在 Python 中,整数、字符串、列表等也都是对象。
二、面向对象特征介绍
课件介绍的三大特征是:封装、继承、多态。它们与上一节的“三大思想特点”含义不同:思想特点帮助我们理解这种编程方式,三大特征则概括了组织对象和复用行为的重要机制。
这一阶段先理解各自的作用,后续再学习具体语法。
2.1 封装
封装强调把相关的数据与操作组织起来,隐藏适当的实现细节,对外提供明确的访问方式。
例如,使用手机拍照时,我们调用拍照功能,无须每次重新处理相机启动、图像采集等内部步骤。在代码中,也可以把某类事物的属性和行为组织到同一个类中。
可以先用课件中的表达建立认识:类 = 属性 + 行为。
封装有助于复用代码,并通过合理限制访问方式减少误操作。本篇先学习类如何组织属性和行为,属性的私有化留到后续课程。
2.2 继承
继承表示子类可以继承父类的成员,例如属性和行为。
假设已经有一个描述车辆共性的类,再编写具体类型的车辆时,就可以考虑复用已有的共性功能。继承的一个重要作用是提高代码的复用性。
本篇暂时不编写父类、子类,只需先理解:已有的共同能力,可以成为进一步扩展的基础。
2.3 多态
按照课件的表述,多态可以理解为:同一个函数接收不同的对象,产生不同的执行效果。
例如,一个函数负责调用对象的“发声”行为,传入猫对象时得到猫叫,传入狗对象时得到狗叫。函数使用统一的调用方式,具体行为由接收到的对象决定。
| 封装 | 组织相关的属性与行为,提供明确的访问方式 |
| 继承 | 在已有类的基础上复用和扩展成员 |
| 多态 | 通过统一的调用方式,让不同对象表现各自的行为 |
接下来先建立类、对象、属性和行为的基础,再逐步学习这些特征的具体实现。
三、入门案例_汽车类
3.1 先区分类、对象、属性与行为
面对一个面向对象需求,可以先从下面四个概念入手:
| 类 | 对同类事物共同特征和行为的抽象描述 | 汽车类 Car |
| 对象 | 类的具体实例 | 某一辆具体的汽车 |
| 属性 | 用来描述对象的特征或状态 | 颜色、品牌、轮胎数 |
| 行为 | 用来描述对象能够完成的操作,在这里通过方法实现 | 跑、启动、停止 |
“汽车”可以概括一类事物,“一辆红色、四个轮胎的汽车”则指向一个具体实例。类与对象的关系,可以理解为共同描述与具体实例的关系。
在 Python 中,使用 class 定义类,在类中使用 def 定义方法。类名通常遵循大驼峰命名法,例如 Car、Phone。
3.2 第一步:定义汽车类
当前需求是定义一个汽车类,并为它提供“跑”的行为。先看练习中的类定义:
class Car:
#属性
#行为
def run(self):
print(f'汽车会跑!')
这里需要注意四点:
定义类是在描述它具有什么行为,并没有执行 run。只有后面调用方法,方法体中的输出语句才会执行。
代码中的 #属性 只是注释,不会自动创建颜色、轮胎数等属性;对象属性的设置将在后面学习。
3.3 第二步:创建对象并调用方法
定义好类以后,需要创建具体对象,再通过对象调用方法:
c1=Car()
#3.调用Car类中的run()
c1.run()
Car() 创建一个 Car 类的实例,变量 c1 引用这个实例。c1.run() 则表示调用这个对象的 run 方法。
当前程序的执行关系是:定义 Car → 创建对象并交给 c1 引用 → 调用 c1.run() → 执行方法体。
运行结果:
汽车会跑!
记住这里的调用结构:对象名.方法名()。点号用于访问对象的成员,后面的括号表示调用方法。
3.4 完整原代码:01_定义汽车类.py
"""
案例:
演示定义汽车类,使用类中的成员
面向对象核心概念:
类:抽象的概念,看不见摸不着,是属性(名词)和行为(动词)的集合
对象:类的具体体现和实现,是类的实例化对象
属性(名词):用来描述汽车的外在特征的,比如颜色、品牌、价格等
行为(动词):用来描述汽车的功能的,比如启动、停止、加速等
定义类的格式:
class 类名:
#属性
#行为
需求:
定义汽车类,又有跑的行为
"""
#定义汽车类
class Car:
#属性
#行为
def run(self):
print(f'汽车会跑!')
#2.创建汽车的对象
c1=Car()
#3.调用Car类中的run()
c1.run()
四、self关键字介绍
4.1 self代表什么
本节沿用课件“self关键字介绍”的章节名称。需要先准确区分:self 不是 Python 关键字,而是实例方法第一个参数的惯用名称。 定义本篇这样的实例方法时,需要把它写在参数列表中。
通过对象调用实例方法时,Python 会自动把这个对象传给方法的第一个参数。因此,self 表示本次调用所对应的当前实例。
可以用一句话记忆:哪个对象调用这个实例方法,方法中的 self 就指向哪个对象。
| c1.run() | c1 引用的对象 |
| c2.run() | c2 引用的对象 |
self 不是固定指向最先创建的对象,而是由本次调用决定。一个类可以创建多个对象,方法通过 self 访问当前这个对象的信息。
下面两份完整练习保留原注释,其中“self关键字”等用语按本节上面的准确解释理解。
4.2 案例1:观察self与调用对象的关系
(1)在方法中打印self
练习在 run 方法中增加了输出:
def run(self):
print("汽车会跑!")
print(f"我是run函数,self的值是:{self}")
第一条语句表示汽车在执行“跑”的行为,第二条语句用于观察当前对象。这里的字符串带有 f 前缀,{self} 才会被替换为对象的字符串表示。
如果省略 f,花括号中的内容就只是普通文本,输出中会直接出现 {self},无法用于观察对象。
(2)分别用两个对象调用run
c1=Car()
print(f'c1对象:{c1}')
#通过对象名.的形式,可以调用Car()中的run()
c1.run()
#继续创建汽车类的对象
c2=Car()
print(f'c2对象:{c2}')
#通过对象名.的形式,可以调用Car()中的run()
c2.run()
运行时,c1 与 c2 分别引用两个不同的对象。可以对照下面的输出结构阅读结果;对象标识A、对象标识B 是示意占位,实际运行时会显示形如 <__main__.Car object at 0x…> 的内容。
c1对象:对象标识A
汽车会跑!
我是run函数,self的值是:对象标识A
c2对象:对象标识B
汽车会跑!
我是run函数,self的值是:对象标识B
应当观察的是对应关系:打印 c1 和在 c1.run() 内打印 self,对应同一个对象;换成 c2 调用时,方法中的 self 也随之指向 c2 对应的对象。每次运行显示的具体十六进制值可能变化,不需要记忆。
调用 c1.run() 时,不需要手动再把 c1 放进括号,因为 Python 已经自动传入了当前对象。这里的 self 是方法参数,不是程序自动为函数添加的“属性”。
(3)完整原代码:02_self介绍.py
"""
案例:
self关键字的介绍
self介绍:
概述:
它是python内置的关键字,用于表示本类当前对象的引用
作用:
1个类是可以有多个对象的,这个多个对象都可以通过对象名.的方式访问类中的行为(函数)
函数默认有self属性,函数用self来区分到底是哪个对象调用的函数
大白话:
谁调用函数,self就代表哪个对象
"""
#创建汽车类
class Car:
#属性
#行为
def run(self):
print("汽车会跑!")
print(f"我是run函数,self的值是:{self}")
#2.创建汽车类的对象
c1=Car()
print(f'c1对象:{c1}')
#通过对象名.的形式,可以调用Car()中的run()
c1.run()
#继续创建汽车类的对象
c2=Car()
print(f'c2对象:{c2}')
#通过对象名.的形式,可以调用Car()中的run()
c2.run()
4.3 案例2:通过self在类内访问其他方法
(1)类外调用与类内调用
前面的 c1.run() 写在类外,通过对象名调用方法。如果需要在同一个对象的另一个实例方法中调用 run,就可以使用 self.run()。
练习中的汽车类同时定义了 run 和 work:
class Car:
#属性
#行为
#1.run函数
def run(self):
print(f"{self}汽车会跑!")
#2.work函数
def work(self):
print(f"我是work函数,我的self值是:{self}")
self.run()
work 中的 self.run() 表示:继续让当前对象执行 run 方法。 它没有创建新汽车,也没有切换到另一个对象。
(2)跟踪一次完整调用
类外调用的原语句是:
c1.work()
这次调用可以按下面的顺序理解:
当类外改用 c2.work() 时,整个过程相同,只是当前对象变成了 c2 引用的对象。
这里不能把 self.run() 随意写成裸调用 run()。普通的名称查找不会自动把类中同名的方法当作当前对象的方法;本例需要通过 self 明确访问对象的成员。
| 类外调用实例方法 | c1.run() | 让指定对象执行方法 |
| 实例方法内调用同一对象的方法 | self.run() | 让当前对象继续执行另一个方法 |
(3)完整原代码:03_类内访问函数.py
"""
案例:
演示通过self关键字实现 再类内访问其他函数
self关键字‘
概述:
代表本类当前对象的引用,谁(哪个对象)调用,self就代表谁
作用:
用于函数区分不同的对象
总结:
1在类外访问类中的行为:需要通过对象名.的形式访问
2在类内访问类中的行为:需要通过self.的形式访问
"""
# 需求:定义汽车类的对象,类中有run()函数,并在work()函数中调用run()函数,创建该类的对象,调用上述函数
class Car:
#属性
#行为
#1.run函数
def run(self):
print(f"{self}汽车会跑!")
#2.work函数
def work(self):
print(f"我是work函数,我的self值是:{self}")
self.run()
#在类外访问Car中的行为(函数)
c1 = Car()
print(f'c1对象:{c1}')
c1.run()
print('-'*40)
c1.work()
print(('='*40))
#再次创建对象
c2 = Car()
print(f'c2对象:{c2}')
c2.run()
print('-'*40)
c2.work()
五、入门案例_手机类
5.1 从需求中找出类与行为
现在把汽车案例中学到的结构应用到新的需求:定义手机类,使手机对象能够开机、拍照、关机。
可以先完成下面的对应关系,再开始写代码:
| 手机这一类事物 | Phone 类 |
| 一部具体手机 | Phone() 创建的对象 |
| 开机 | open 方法 |
| 拍照 | take_photo 方法 |
| 关机 | close 方法 |
这个练习暂时只描述行为,不要求记录品牌、价格等属性。建立类时,应先围绕当前需求组织必要的成员。
5.2 定义手机类
class Phone:
#属性
#行为
def open(self):
print(f"{self}手机开机了")
def take_photo(self):
print(f"{self}手机拍照了")
def close(self):
print(f"{self}手机关机了")
三个方法都显式声明了 self。它们定义在同一个类中,但在通过不同对象调用时,接收的当前对象可以不同。
方法名表达功能:open 对应开机,take_photo 对应拍照,close 对应关机。这些是当前类定义的方法,通过手机对象访问。
5.3 创建对象,按需求调用方法
p1=Phone()
p1.open()
p1.take_photo()
p1.close()
调用顺序是“开机 → 拍照 → 关机”,因此输出也按这个顺序出现。完整代码还创建了第二个对象 p2,让它依次执行相同的方法。
这个练习同时巩固了两个认识:同一个类可以创建多个对象;程序实际执行哪些行为、按什么顺序执行,由方法调用决定。 类中方法的排列位置,不会让这些方法自动依次运行。
需要区分练习中的行为演示和实际设备控制:这里三个方法都只执行 print,用于展示调用过程,没有连接真实手机,也没有实现开关机状态管理。
5.4 完整原代码:04_example_手机类.py
"""
案例:
定义手机类,要求能开机、关机、拍照
回顾:
定义类的格式:
class 类名:
#属性
#行为(方法)
访问类中成员的方式:
类内:self.属性名
类外:对象名.属性名
"""
#1.定义手机类
class Phone:
#属性
#行为
def open(self):
print(f"{self}手机开机了")
def take_photo(self):
print(f"{self}手机拍照了")
def close(self):
print(f"{self}手机关机了")
#2.创建手机类对象,访问其成员
p1=Phone()
p1.open()
p1.take_photo()
p1.close()
#继续创建手机类对象,访问其成员
p2=Phone()
p2.open()
p2.take_photo()
p2.close()
六、类外_获取和设置对象的属性
6.1 属性用来保存对象的信息
到目前为止,汽车对象可以执行 run,但还没有保存颜色、轮胎数等信息。要描述某一辆汽车,就需要为对象设置属性。
属性保存对象的特征或状态,方法组织对象能够完成的操作。 例如,颜色与轮胎数适合作为属性,“跑”适合作为方法。
在本篇这样的普通类中,可以在创建对象之后,通过 对象名.属性名 = 属性值 给这个对象新增或设置属性;通过 对象名.属性名 读取属性值。
| 设置属性 | 对象名.属性名 = 属性值 | c1.color = "红色" |
| 获取属性 | 对象名.属性名 | c1.color |
| 调用方法 | 对象名.方法名() | c1.run() |
读取属性与调用方法的一个直接区别是:这里读取颜色不加调用括号,而执行 run 需要括号。
6.2 为第一辆汽车设置属性并读取
c1 = Car()
c1.color = "红色"
c1.number=4
c1.run()
print(f"c1对象的颜色是:{c1.color},轮胎数是:{c1.number}")
这段代码依次完成以下操作:
其中,属性输出为:
c1对象的颜色是:红色,轮胎数是:4
在这里,赋值语句左侧的点号说明数据保存到哪个对象上。c1.color 是对象的属性,和单独一个局部变量 color 不是同一种访问形式。
6.3 为什么第二个对象没有这些属性
完整练习又创建了第二辆汽车:
c2 = Car()
c2.run()
c2.run() 可以执行,因为 run 是在 Car 类中定义的实例方法,两个汽车对象都可以通过所属类访问它。
但是,前面的两次属性赋值明确作用于 c1 引用的对象,代码没有为 c2 设置 color 和 number。因此,在当前示例中,不能直接读取 c2.color 或 c2.number。
| c1引用的对象 | "红色" | 4 | 可以 |
| c2引用的对象 | 尚未设置 | 尚未设置 | 可以 |
完整代码把访问 c2 属性的语句注释掉了。如果尝试直接读取这些尚不存在的属性,会触发 AttributeError。属于同一个类,不代表一个实例动态新增的属性会自动添加到其他实例上。
这里讨论的是当前示例中的实例属性,类属性等机制将在后续课程中学习。
6.4 完整原代码:05_类外设置和获取对象的属性.py
"""
案例:
演示如何在类外设置和获取对象的属性
类外,设置和获取对象的属性格式如下
对象名.属性名 = 属性值
特点:该属性独属于这个对象,即类的其他对象没有这个属性
类外:
获取对象的属性的格式如下:
对象名.属性名
"""
#创建汽车类
class Car:
#属性
#行为
def run(self):
print(f"{self}汽车会跑!")
#创建一个汽车对象
c1 = Car()
c1.color = "红色"
c1.number=4
c1.run()
print(f"c1对象的颜色是:{c1.color},轮胎数是:{c1.number}")
#创建另一个汽车对象
c2 = Car()
c2.run()
# print(f"c2对象的颜色是:{c2.color},轮胎数是:{c2.number}")会报错
七、类内_获取对象的属性
7.1 在实例方法中通过self读取属性
上一节是在类外通过 c1.color、c1.number 读取属性。如果想让汽车对象自己展示信息,就可以定义一个 show 方法,在方法内部访问当前对象的属性。
这里仍然使用 self:self.color 读取当前对象的颜色,self.number 读取当前对象的轮胎数。
访问关系没有改变,改变的是所处的位置:类外使用指向具体对象的变量,实例方法内部使用参数 self。
| 类外,本次使用对象变量c1 | c1.color | c1.number |
| 当前对象的实例方法内部 | self.color | self.number |
7.2 定义show方法
下面摘录练习中的 show 方法。它定义在 Car 类内部,前置片段仅去掉外层缩进,便于单独阅读:
def show(self):
print(f"我是show函数,对象的颜色是{self.color} ,轮胎数是{self.number}")
这个方法先读取当前对象的两个属性,再把它们插入输出字符串。self 不会自动生成这两个属性,因此在调用 show 之前,当前对象必须已经具备相应属性。
这也解释了为什么在方法里直接写 color 或 number 不能替代这里的属性访问:它们只是普通名称,不能明确表示当前实例上的数据。
7.3 先为对象设置属性,再调用show
c1 = Car()
#3.给c1赋予属性—颜色和轮胎数作为类外设置属性
c1.color = "红色"
c1.number=4
#4.类外访问属性
print(f"c1对象的颜色是:{c1.color},轮胎数是:{c1.number}")
#5.类外访问行为(类中的函数)
c1.run()
c1.show()
按执行顺序理解:
show 中对应的属性输出是:
我是show函数,对象的颜色是红色 ,轮胎数是4
因此,类外的 c1.color 与本次调用中类内的 self.color,访问的是同一个对象上的同一个属性。类外保存的数据可以在实例方法内使用,不需要在调用 show() 时把颜色和轮胎数重新作为参数传入。
7.4 为什么c2能调用run,却不能直接调用show
完整代码最后创建了 c2 并调用 run,但把 c2.show() 保留为注释。
原因在于两个方法对数据的依赖不同:
| run() | 不需要 | 可以正常执行 |
| show() | 需要 | 缺少这些属性,执行时会报错 |
c2 可以访问 show 这个方法,问题发生在执行方法体时:self 指向 c2 引用的对象,而这个对象没有设置 color 和 number,因此读取时会触发 AttributeError。
这与“类中没有定义 show 方法”是不同的问题。阅读报错时,需要区分是找不到方法,还是方法内部访问了尚不存在的属性。
7.5 把本篇的访问方式连起来
到这里,可以用下面这张表检查自己是否理解了类内与类外的关系:
| 定义类 | class Car: |
| 创建对象 | c1 = Car() |
| 类外调用方法 | c1.run()、c1.show() |
| 实例方法内调用当前对象的其他方法 | self.run() |
| 类外设置对象属性 | c1.color = "红色" |
| 类外读取对象属性 | c1.color |
| 实例方法内读取当前对象属性 | self.color |
阅读下面的完整代码时,可以试着回答两个问题:c1.show() 内部的 self 指向谁?为什么同样是 Car 的对象,c2.run() 可以执行,而直接调用 c2.show() 会失败?答案都取决于当前对象以及它已经具有的属性。
本篇先采用“创建对象后,在类外设置属性”的方式。后续会学习 __init__(),在对象创建时完成属性初始化。其他实例方法中同样可以通过 self.属性名 = 属性值 设置属性;初始化方法的作用,是让需要的初始数据在合适的时机准备好。
7.6 完整原代码:06_类内获取对象属性.py
"""
案例:
演示类内如何获取对象属性
回顾(总结):
1.类外访问类中的成员,可以通过对象名.的形式访问
2.类内访问类中的成员,可以通过self.的形式访问
3.类外通过对象名.属性名=属性值,可以设置对象的属性,但是只有对象自己有这个属性,其他对象没有这个属性
细节:
类内如何设置属性,要结合__init__()来实现,后面的文件会详细讲解
"""
#1.定义一个汽车类
class Car:
#属性
#行为
def run(self):
print(f"{self}汽车会跑!")
def show(self):
print(f"我是show函数,对象的颜色是{self.color} ,轮胎数是{self.number}")
#2.创建一个汽车对象
c1 = Car()
#3.给c1赋予属性—颜色和轮胎数作为类外设置属性
c1.color = "红色"
c1.number=4
#4.类外访问属性
print(f"c1对象的颜色是:{c1.color},轮胎数是:{c1.number}")
#5.类外访问行为(类中的函数)
c1.run()
c1.show()
print('-'*40)
#6.继续创建汽车对象,分别尝试调用run()和show()函数
c2 = Car()
c2.run()
# c2.show()会报错

![打卡信奥刷题(3676)用C++实现信奥题 P12271 [蓝桥杯 2024 国 Python B] 括号与字母-171主机测评](https://www.171host.com/wp-content/uploads/2026/11/20261107050125-6aeeb0a5a5ade-220x150.png)