【Python-Day 29】万物皆对象:详解 Python 类的定义、实例化与 `__init__` 方法
Langchain系列文章目录
01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来
Python系列文章目录
PyTorch系列文章目录
机器学习系列文章目录
深度学习系列文章目录
Java系列文章目录
JavaScript系列文章目录
Python系列文章目录
01-【Python-Day 1】告别编程恐惧:轻松掌握 Python 安装与第一个程序的 6 个步骤
02-【Python-Day 2】掌握Python基石:变量、内存、标识符及int/float/bool数据类型
03-【Python-Day 3】玩转文本:字符串(String)基础操作详解 (上)
04-【Python-Day 4】玩转文本:Python 字符串常用方法深度解析 (下篇)
05-【Python-Day 5】Python 格式化输出实战:%、format()、f-string 对比与最佳实践
06- 【Python-Day 6】从零精通 Python 运算符(上):算术、赋值与比较运算全解析
07-【Python-Day 7】从零精通 Python 运算符(下):逻辑、成员、身份运算与优先级规则全解析
08-【Python-Day 8】从入门到精通:Python 条件判断 if-elif-else 语句全解析
09-【Python-Day 9】掌握循环利器:for 循环遍历序列与可迭代对象详解
10-【Python-Day 10】Python 循环控制流:while 循环详解与 for 循环对比
11-【Python-Day 11】列表入门:Python 中最灵活的数据容器 (创建、索引、切片)
12-【Python-Day 12】Python列表进阶:玩转添加、删除、排序与列表推导式
13-【Python-Day 13】Python 元组 (Tuple) 详解:从创建、操作到高级应用场景一网打尽
14-【Python-Day 14】玩转Python字典(上篇):从零开始学习创建、访问与操作
15-【Python-Day 15】深入探索 Python 字典 (下):常用方法、遍历、推导式与嵌套实战
16-【Python-Day 16】代码复用基石:详解 Python 函数的定义与调用
17-【Python-Day 17】玩转函数参数(上):轻松掌握位置、关键字和默认值
18-【Python-Day 18】玩转函数参数(下):*args 与 **kwargs 终极指南
19-【Python-Day 19】函数的回响:深入理解 return
语句与返回值
20-【Python-Day 20】揭秘Python变量作用域:LEGB规则与global/nonlocal关键字详解
21-【Python-Day 21】一行搞定!Python lambda 匿名函数的妙用与实战
22-【Python-Day 22】代码的基石:模块(Module)的导入与使用详解
23-【Python-Day 23】Python 模块化编程实战:创建、导入及 sys.path 深度解析
24-【Python-Day 24】告别杂乱代码!一文掌握 Python 包(Package)的创建与使用
25-【Python-Day 25】玩转数字:精通 math 与 random 模块,从数学运算到随机抽样
26-【Python-Day 26】解锁时间魔法:深入解析 time 与 datetime 模块
27-【Python-Day 27】轻松驾驭操作系统:精通 os 与 sys 模块核心功能
28-【Python-Day 28】从指令到蓝图:Python面向对象编程(OOP)入门指南
29-【Python-Day 29】万物皆对象:详解 Python 类的定义、实例化与 __init__
方法
文章目录
- Langchain系列文章目录
- Python系列文章目录
- PyTorch系列文章目录
- 机器学习系列文章目录
- 深度学习系列文章目录
- Java系列文章目录
- JavaScript系列文章目录
- Python系列文章目录
- 前言
- 一、回顾:什么是类和对象?
- 1.1.1 蓝图与实体
- 1.1.2 属性与行为
- 二、定义一个简单的类:`class` 关键字
- 2.1.1 基本语法
- 2.1.2 实例化:从类创建对象
- 三、灵魂所在:构造方法 `__init__` 与 `self`
- 3.1.1 什么是构造方法 `__init__`?
- 3.1.2 `self` 的奥秘
- 3.1.3 定义带属性的类
- 四、访问对象的属性和方法
- 4.1.1 访问实例属性
- 4.1.2 修改实例属性
- 4.1.3 定义实例方法
- 4.1.4 调用实例方法
- 五、实战场景:创建一个“英雄”类
- 5.1.1 需求分析
- 5.1.2 代码实现
- 5.1.3 模拟对战
- 六、总结
前言
在上一篇文章中,我们共同揭开了面向对象编程(OOP)的神秘面纱,理解了“类(Class)”和“对象(Object)”这两个核心概念的理论基础。我们知道,类是创建对象的蓝图,而对象则是类的具体实例。
理论是行动的指南,但真正的掌握源于实践。从本篇文章开始,我们将正式从理论迈向实战,动手创建并使用我们自己的 Python 类。这篇文章将是您踏入 Python 面向对象世界的第一步,也是至关重要的一步。我们将详细讲解如何使用 class
关键字定义一个类,深入剖析构造方法 __init__
的作用,并彻底搞懂那个让许多初学者困惑的 self
参数究竟是什么。
准备好了吗?让我们一起从零开始,构建属于你自己的第一个 Python 类!
一、回顾:什么是类和对象?
在动手编码之前,让我们通过一个生动的类比,快速回顾一下类与对象的关系,为后续的实践打下坚实的基础。
1.1.1 蓝图与实体
想象一下,一位汽车设计师绘制了一张“汽车设计蓝图”。
-
类 (Class):就是这张设计蓝图。它详细定义了汽车应该具备的所有通用特性(属性),比如品牌、颜色、型号,以及能够执行的功能(方法),比如启动、加速、刹车。蓝图本身并不是一辆可以驾驶的汽车,它只是一个模板或规范。
-
对象 (Object):就是根据这张蓝图制造出来的每一辆具体的汽车。每一辆车(对象)都遵循蓝图(类)的设计,但又拥有自己独特的属性值。例如,一辆是“红色的法拉利”,另一辆是“黑色的特斯拉”。它们都是“汽车”这个类的实例,但它们是相互独立的实体。
1.1.2 属性与行为
- 属性 (Attribute):对象的特征或状态,在代码中通常表现为变量。例如,对于一个“人”类,
name
、age
、height
就是属性。 - 方法 (Method):对象的行为或能力,在代码中通常表现为函数。例如,“人”可以
eat()
、sleep()
、work()
。
我们的目标就是学习如何用 Python 代码来描述这张“蓝图”(定义类),并根据它来创建具体的“实体”(实例化对象)。
二、定义一个简单的类:class
关键字
在 Python 中,定义类的语法非常直观,我们使用 class
关键字。
2.1.1 基本语法
让我们从最简单的形式开始,创建一个代表“狗”的类。
class Dog:pass
代码解析:
class Dog:
:class
是关键字,告诉 Python 我们正在定义一个类。Dog
是我们给这个类起的名字。类名的命名规范通常遵循 大驼峰命名法 (PascalCase),即每个单词的首字母都大写,例如MyClassName
。pass
:是一个占位符语句,表示“什么都不做”。因为 Python 的语法要求代码块(如类定义、函数定义等)不能为空,所以当我们想创建一个最精简的空类时,就需要使用pass
。
2.1.2 实例化:从类创建对象
我们已经有了 Dog
类的“蓝图”,现在就可以根据它来创建具体的“狗”对象了。这个过程称为实例化 (Instantiation)。
语法非常简单,就像调用一个函数一样:
# 实例化一个 Dog 对象,并将其赋值给变量 my_dog
my_dog = Dog()# 我们可以再创建一个
another_dog = Dog()# 打印这两个对象,看看会发生什么
print(my_dog)
print(another_dog)# 使用 type() 函数查看它们的类型
print(type(my_dog))
输出结果 (内存地址会不同):
<__main__.Dog object at 0x000001A8D9B43E20>
<__main__.Dog object at 0x000001A8D9B43E50>
<class '__main__.Dog'>
结果分析:
my_dog = Dog()
这行代码就完成了一次实例化。它创建了一个Dog
类的实例,并把这个实例(对象)的引用存放在了my_dog
这个变量里。- 打印
my_dog
和another_dog
,我们看到它们是两个不同的Dog
对象,位于内存中的不同地址 (0x...
)。这证明了每个实例都是独一无二的。 type(my_dog)
的输出<class '__main__.Dog'>
明确告诉我们,my_dog
这个变量的类型是我们刚刚定义的Dog
类。
目前,我们的 Dog
类还只是一个空壳,它没有具体的属性(比如名字、年龄)和行为(比如吠叫)。接下来,我们将学习如何让它变得丰满起来。
三、灵魂所在:构造方法 __init__
与 self
要让每个 Dog
对象都拥有自己独特的名字和年龄等信息,我们需要在对象被创建的那一刻就为它设定好这些初始属性。这就需要用到一个非常特殊的函数——构造方法 __init__
。
3.1.1 什么是构造方法 __init__
?
__init__
是 Python 类中的一个特殊方法(也称为“魔术方法”或“双下划线方法”)。它的名字前后都有两个下划线。
它的核心作用是:在类的实例被创建时,自动被调用,用于完成对象的初始化工作。
你可以把它想象成工厂流水线上的“初始化工位”。每当一个新产品(对象)从生产线上下来(被创建),它都会自动经过这个工位,进行喷漆、贴标签等一系列初始化操作。
3.1.2 self
的奥秘
在介绍 __init__
的用法之前,我们必须先理解 self
。self
是类中方法(函数)的第一个参数,它是一个让无数初学者感到困惑的概念。
一句话解释 self
:self
代表的就是类的实例(对象)本身。
当一个对象调用它的方法时,Python 会自动将这个对象本身作为第一个参数传递给该方法。这个参数,按照约定俗成的惯例,我们都将其命名为 self
。
举个例子:
当我们执行 my_dog = Dog("Buddy", 3)
来创建一个 Dog
对象时,Python 内部大致会做两件事:
- 先在内存中创建一个空的
Dog
对象。 - 然后自动调用
__init__
方法,并将这个刚刚创建的空对象作为第一个参数传递进去,即Dog.__init__(刚刚创建的空对象, "Buddy", 3)
。在这个__init__
方法内部,self
就指向了这个“刚刚创建的空对象”。
通过 self
,我们就可以在类的内部访问和操作这个对象自身的属性和方法了。
3.1.3 定义带属性的类
现在,让我们结合 __init__
和 self
来改造我们的 Dog
类,让它在创建时就能拥有名字、品种和年龄。
class Dog:# 定义构造方法def __init__(self, name, breed, age):"""初始化一只新的小狗对象。"""print(f"初始化开始... 一只新的小狗对象正在被创建!")# 将传入的参数值赋给实例的属性# self.name 创建了一个属于该实例的属性 nameself.name = nameself.breed = breedself.age = ageprint(f"初始化完成!你好,我叫 {self.name}。")# 实例化时,在类名后的括号里传入 __init__ 方法需要的参数 (除了 self)
dog1 = Dog("旺财", "中华田园犬", 2)
print("-" * 20)
dog2 = Dog("Buddy", "金毛巡回犬", 3)
输出结果:
初始化开始... 一只新的小狗对象正在被创建!
初始化完成!你好,我叫 旺财。
--------------------
初始化开始... 一只新的小狗对象正在被创建!
初始化完成!你好,我叫 Buddy。
代码解析:
def __init__(self, name, breed, age):
:我们定义了构造方法。它的第一个参数必须是self
。后面的name
,breed
,age
是我们希望在创建对象时接收的外部数据。self.name = name
:这是__init__
方法的核心。self.name
:表示“为当前这个对象(self
)创建一个名为name
的属性”。这种与self
关联的属性称为实例属性 (Instance Attribute)。= name
:将传入的参数name
的值赋给这个实例属性。
- 实例化过程:
- 当我们执行
dog1 = Dog("旺财", "中华田园犬", 2)
时,Python 自动调用__init__
方法,并将dog1
这个实例传给self
,“旺财” 传给name
,“中华田园犬” 传给breed
,2 传给age
。 - 方法内部,
self.name = "旺财"
、self.breed = "中华田园犬"
、self.age = 2
等语句就为dog1
这个具体对象设置了它独有的属性。 dog2
的创建过程同理,它也拥有自己的一套独立的属性值。
- 当我们执行
四、访问对象的属性和方法
创建了带有属性的对象后,我们自然需要一种方式来使用这些属性,并命令对象去执行某些行为(调用方法)。
4.1.1 访问实例属性
我们可以使用“点 (.
) 操作符”来访问一个对象的属性。
语法:对象名.属性名
# 延续上面的代码
print(f"{dog1.name} 是一只 {dog1.breed}。")
print(f"{dog2.name} 今年 {dog2.age} 岁了。")
输出结果:
旺财 是一只 中华田园犬。
Buddy 今年 3 岁了。
dog1.name
精确地获取了 dog1
这个对象的 name
属性值,而 dog2.name
获取的是 dog2
对象的,两者互不干扰。
4.1.2 修改实例属性
实例属性的值是可变的(除非我们使用更高级的技巧来限制)。修改它同样使用点操作符。
print(f"过生日之前, {dog1.name} 是 {dog1.age} 岁。")# 修改 age 属性
dog1.age = dog1.age + 1
# 或者使用更简洁的写法
# dog1.age += 1print(f"过完生日后, {dog1.name} 现在是 {dog1.age} 岁了。")
输出结果:
过生日之前, 旺财 是 2 岁。
过完生日后, 旺财 现在是 3 岁了。
4.1.3 定义实例方法
光有属性还不够,对象应该有自己的行为。在类中定义的函数,我们称之为方法 (Method)。与实例属性一样,能访问实例属性的方法称为实例方法 (Instance Method)。
实例方法的第一个参数也必须是 self
,这样它才能访问到调用该方法的对象的内部属性。
让我们给 Dog
类添加两个方法:bark()
(吠叫) 和 describe()
(自我介绍)。
class Dog:def __init__(self, name, breed, age):self.name = nameself.breed = breedself.age = age# 定义一个实例方法def bark(self):# self 在这里同样代表调用这个方法的那个实例return f"{self.name} 说: 汪!汪汪!"# 定义另一个使用多个属性的实例方法def describe(self):return f"你好,我是 {self.name}, 我是一只 {self.age} 岁的 {self.breed}。"
4.1.4 调用实例方法
调用方法和访问属性的方式完全一样,也是使用点操作符,但方法名后面需要加上一对括号 ()
。
语法:对象名.方法名()
dog1 = Dog("旺财", "中华田园犬", 3)
dog2 = Dog("Buddy", "金毛巡回犬", 3)# 调用 dog1 的方法
bark_sound = dog1.bark()
description = dog1.describe()
print(bark_sound)
print(description)print("-" * 20)# 调用 dog2 的方法
print(dog2.bark())
print(dog2.describe())
输出结果:
旺财 说: 汪!汪汪!
你好,我是 旺财, 我是一只 3 岁的 中华田园犬。
--------------------
Buddy 说: 汪!汪汪!
你好,我是 Buddy, 我是一只 3 岁的 金毛巡回犬。
深入理解调用过程:当你执行 dog1.bark()
时,Python 实际上做的是 Dog.bark(dog1)
。它将 dog1
这个实例作为第一个参数(即 self
)传递给了 bark
方法。这就是为什么在方法内部,self
能够引用到 dog1
的所有属性(如 self.name
)。
五、实战场景:创建一个“英雄”类
理论学习后,让我们通过一个更有趣的实战案例来巩固所学知识。假设我们在开发一款简单的文字对战游戏,我们需要创建一个 Hero
类。
5.1.1 需求分析
- 属性: 每个英雄都应该有名字(
name
)、生命值(hp
)和攻击力(ap
)。 - 方法:
attack(target)
:一个英雄可以攻击另一个英雄,对目标造成伤害(减少目标的hp
)。show_status()
:可以显示英雄当前的状态信息。
5.1.2 代码实现
import timeclass Hero:"""代表一个游戏中的英雄角色。"""def __init__(self, name, hp=100, ap=15):"""初始化英雄属性,hp 和 ap 可以有默认值。"""self.name = nameself.hp = hpself.ap = apprint(f"英雄 {self.name} 已降临战场! [HP: {self.hp}, AP: {self.ap}]")def attack(self, target):"""攻击另一个英雄 (target)。target: 另一个 Hero 对象。"""if self.hp <= 0:print(f"{self.name} 已经阵亡,无法发起攻击。")return # 提前结束方法if target.hp <= 0:print(f"攻击失败:{target.name} 已经阵亡。")returnprint(f"【战斗】{self.name} 对 {target.name} 发起了猛烈的攻击!")# 目标英雄的 hp 减少攻击者的 ap 值target.hp -= self.apprint(f" 造成了 {self.ap} 点伤害。")if target.hp <= 0:target.hp = 0 # 避免 hp 出现负数print(f" {target.name} 被击败了!")else:print(f" {target.name} 剩余生命值: {target.hp}")def show_status(self):"""显示英雄当前的状态。"""status = "存活" if self.hp > 0 else "阵亡"print(f"--- 状态面板 [{self.name}] ---")print(f" 生命值 (HP): {self.hp}")print(f" 攻击力 (AP): {self.ap}")print(f" 当前状态: {status}")print(f"--------------------------")
这个 Hero
类比 Dog
类更复杂一些,它不仅有属性,还有一个可以与其他同类对象交互的 attack
方法,这正是面向对象编程强大之处的体现。
5.1.3 模拟对战
现在,让我们创建两位英雄,并让他们进行一场简单的对决!
# 创建两位英雄实例
garen = Hero("盖伦", hp=150, ap=10)
darius = Hero("德莱厄斯", hp=120, ap=12)time.sleep(1) # 暂停1秒,让输出更有节奏感
print("\n===== 战斗开始! =====\n")
time.sleep(1)# 第一回合
darius.attack(garen)
garen.show_status()
time.sleep(1)# 第二回合
garen.attack(darius)
darius.show_status()
time.sleep(1)# 第三回合
darius.attack(garen)
garen.show_status()print("\n===== 战斗结束! =====\n")
运行上述代码,你将看到一场生动的文字直播战斗,这全部是通过我们定义的 Hero
类和它的实例对象之间的交互完成的。
六、总结
恭喜你!通过本文的学习和实践,你已经成功创建并使用了自己的第一个 Python 类。这是你从面向过程思维转向面向对象思维的关键一步。让我们对今天的核心知识点进行梳理和总结:
-
类的定义:使用
class
关键字来声明一个类,类名推荐使用大驼峰命名法(PascalCase
)。类是创建对象的模板。 -
构造方法
__init__
:一个特殊的“魔术方法”,在通过类创建对象时被自动调用。它的主要职责是完成新对象的初始化工作,如设置初始属性。 -
self
参数:是所有实例方法的第一个参数,它代表调用该方法的对象实例本身。通过self
,我们可以在类的内部访问实例的属性和调用其他实例方法。它是一个约定俗成的名称,请务必遵守。 -
实例属性:与特定对象实例绑定的变量(例如
self.name
)。它们在__init__
方法中定义,每个对象都拥有一份自己独立的副本,互不影响。 -
实例化:通过
类名()
的形式创建类的具体实例(对象)。如果__init__
方法有其他参数,需要在括号内提供相应的值。 -
访问与调用:统一使用点 (
.
) 操作符来访问对象的属性(object.attribute
)和调用其方法(object.method()
)。 -
封装思想初探:通过本章的学习,我们已经初步体验了面向对象三大特性之一的“封装”。类将描述对象状态的数据(属性)和操作这些数据的代码(方法)巧妙地“封装”在了一起,形成了一个功能完整的独立单元。
在下一篇文章中,我们将继续深入探索类的世界,学习不同类型的方法(实例方法、类方法、静态方法)及其各自的应用场景。请务必勤加练习,真正消化今天的内容!