第26课:__init__ 方法与对象初始化
所属模块:模块七:面向对象编程 难度:中级 标签:oop, init, constructor, 初始化 上一课:面向对象编程:类与对象 下一课:实例方法、类方法与静态方法
1. 学习目标
通过本课的学习,你将能够:
- 理解
__init__方法在对象创建过程中的核心作用。 - 掌握定义和使用
__init__方法的标准语法。 - 学会使用
self参数来初始化对象的实例属性。 - 能够设计并实现带有合理初始化逻辑的类。
- 了解在
__init__中使用默认参数来提高代码灵活性。
2. 核心概念
在上一课中,我们学习了如何使用 class 关键字定义一个类,并通过调用类名(如 my_dog = Dog())来创建一个对象。但你是否想过,创建对象后,我们如何为每个对象设置其独有的初始状态(比如小狗的品种、名字)呢?这就轮到 __init__ 方法登场了。
通俗比喻:你可以把 __init__ 想象成工厂的**"产品登记处"或婴儿的"出生证明"。当一个新的对象(产品/婴儿)被创建(生产/出生)时,__init__ 方法会自动被调用**一次,专门负责对这个新对象进行"登记"和"初始化",为其赋予初始的特征(属性)。
关键点:
- 特殊方法:
__init__是一个特殊方法(或称为"魔术方法"),它的名字以双下划线开头和结尾。 - 自动调用:你不需要手动调用
__init__。当你执行my_object = ClassName()创建对象时,Python 会自动为你调用它。 - 不是构造函数:从技术上讲,在 Python 中
__new__才是真正创建对象实例的构造方法,而__init__是在实例被创建之后进行初始化的方法。但通常我们只需关心__init__。 - 初始化属性:它的主要任务是接收创建对象时传入的参数,并利用这些参数为对象设置初始的实例属性。
self 参数是什么?
__init__ 方法的第一个参数永远是 self。你可以把它理解为新创建的对象本身。当我们通过 my_dog = Dog("Buddy", "Golden Retriever") 创建对象时,Python 会先把新创建的对象(一个空的 Dog 实例)传给 self 参数,然后再执行 __init__ 里的代码,将数据赋值给 self(也就是这个新对象)的属性。
3. 代码示例
示例 1:基本的 __init__ 方法
class Dog:
# 定义 __init__ 方法,它会在创建 Dog 对象时自动调用
# self 代表正在被创建的那个 Dog 实例
# name 和 breed 是我们期望传入的参数
def __init__(self, name, breed):
print(f"正在创建一只名为 {name} 的狗...")
# 使用 self 关键字来为当前实例(self)设置属性
self.name = name
self.breed = breed
self.tricks = [] # 初始化一个空列表,所有狗实例默认都不会任何技巧
def show_info(self):
print(f"狗名: {self.name}, 品种: {self.breed}")
# 创建对象时,需要提供 __init__ 中除 self 之外的所有参数
my_dog = Dog("Buddy", "Golden Retriever")
your_dog = Dog("Max", "German Shepherd")
my_dog.show_info() # 输出:狗名: Buddy, 品种: Golden Retriever
your_dog.show_info() # 输出:狗名: Max, 品种: German Shepherd
# 验证每只狗都有独立的 tricks 列表
my_dog.tricks.append("shake hands")
print(my_dog.tricks) # 输出:['shake hands']
print(your_dog.tricks) # 输出:[] (your_dog的列表是空的,互不影响)
示例 2:使用默认参数和更复杂的初始化逻辑
class BankAccount:
def __init__(self, owner, balance=0.0):
# owner 是必需参数
# balance 有默认值 0.0,创建账户时可以不提供
self.owner = owner
self.balance = balance
self.is_active = True # 默认账户是活跃的
print(f"为 {self.owner} 创建新账户,初始余额: {self.balance} 元")
def deposit(self, amount):
if amount > 0:
self.balance += amount
print(f"存入 {amount} 元。新余额: {self.balance} 元")
else:
print("存款金额必须大于0!")
def withdraw(self, amount):
if amount > 0 and amount <= self.balance:
self.balance -= amount
print(f"取出 {amount} 元。新余额: {self.balance} 元")
else:
print("取款失败:金额无效或余额不足。")
# 使用默认余额创建账户
acc1 = BankAccount("Alice") # 输出:为 Alice 创建新账户,初始余额: 0.0 元
# 指定初始余额创建账户
acc2 = BankAccount("Bob", 1000) # 输出:为 Bob 创建新账户,初始余额: 1000 元
acc1.deposit(500) # 输出:存入 500 元。新余额: 500 元
acc2.withdraw(200) # 输出:取出 200 元。新余额: 800 元
示例 3:在 __init__ 中调用其他方法
class Computer:
def __init__(self, cpu, ram_gb, storage_type="SSD"):
self.cpu = cpu
self.ram_gb = ram_gb
self.storage_type = storage_type
self.is_on = False
# 在初始化时,可以调用其他方法
self._run_self_check()
def _run_self_check(self):
"""模拟开机自检"""
print(f"正在对 {self.cpu} 进行自检...")
print("自检完成。系统就绪。")
def power_on(self):
if not self.is_on:
print("电脑开机中...")
self.is_on = True
else:
print("电脑已经处于开机状态。")
my_pc = Computer("Intel i7", 16)
# 输出:
# 正在对 Intel i7 进行自检...
# 自检完成。系统就绪。
my_pc.power_on() # 输出:电脑开机中...
4. 实践练习
请根据要求编写代码:
练习题 1(基础):创建一个 Book 类。
- 要求:
__init__方法接收两个参数:title(书名) 和author(作者)。- 在
__init__中将这两个参数分别赋值给实例属性self.title和self.author。 - 创建一个
Book对象my_book,书名为 "Python编程:从入门到实践",作者为 "Eric Matthes"。 - 打印出该书的信息,格式为:
《[书名]》,作者:[作者]。
- 预期输出:
《Python编程:从入门到实践》,作者:Eric Matthes
练习题 2(进阶):改进 Book 类。
- 要求:
- 为
__init__增加一个可选参数price,默认值为29.9。 - 添加一个实例属性
self.price来存储价格。 - 添加一个实例属性
self.pages_read,初始化为0。 - 添加一个实例方法
read(pages),该方法将传入的pages数加到self.pages_read上。 - 创建对象并测试
read方法。
- 为
- 预期输出示例:
开始阅读,读了 50 页。 目前已读页数:50
练习题 3(综合):设计一个 Student 类。
- 要求:
- 初始化时需要传入
name(姓名) 和student_id(学号)。 - 还有一个可选参数
courses,默认值为空列表[],用于存储学生选修的课程列表。 - 提供一个方法
enroll(course),用于将课程名加入到学生的课程列表中(注意避免重复添加)。 - 提供一个方法
show_courses(),用于打印该学生的所有选修课。 - 创建一个学生对象,进行选课操作,并打印课程列表。
- 初始化时需要传入
- 预期输出示例:
学生 张三 (学号: S001) 选修了: ['数学', '物理', 'Python']
5. 常见错误
- 忘记
self参数:定义__init__或其他实例方法时,第一个参数必须是self。忘记写会导致调用时参数数量不匹配的TypeError。# 错误示范 class BadDog: def __init__(name, breed): # 错误!缺少 self self.name = name # my_dog = BadDog("Buddy") 会引发 TypeError - 在
__init__中误用return:__init__方法的作用是初始化对象,它不应该有返回值(除了默认的None)。试图返回一个非None的值会导致TypeError。class BadClass: def __init__(self): return 42 # 错误!__init__ 不应返回值 # obj = BadClass() 会引发 TypeError: __init__() should return None - 混淆类变量和实例变量:在
__init__中定义的变量(使用self.)是实例变量,每个对象一份。而直接在类体内(方法外)定义的变量是类变量,所有实例共享。class Counter: count = 0 # 这是类变量,所有实例共享 def __init__(self): self.instance_id = id(self) # 这是实例变量,每个对象不同 Counter.count += 1 # 修改类变量,会影响所有实例 - 初始化逻辑过于复杂:
__init__应专注于设置初始状态。如果初始化过程非常复杂(如连接数据库、读取大文件),可能违反"单一职责原则"。可以考虑在__init__中调用一个专门的初始化方法,或使用工厂模式。
6. 小结
本课我们深入学习了面向对象编程中至关重要的 __init__ 方法:
- 核心作用:
__init__是对象初始化方法,在对象创建后自动调用一次,用于设置对象的初始属性。 self关键字:self代表当前正在被创建或操作的实例对象本身。通过self,我们可以将数据绑定到这个具体的对象上。- 参数设计:
__init__的参数(除self外)就是在创建对象时ClassName(arg1, arg2, ...)中需要传入的参数。善用默认参数可以增加类的灵活性。 - 实践意义:一个设计良好的
__init__能确保每个对象在诞生时就处于一个有效、一致且可用的状态,这是构建健壮、可预测程序的基础。
记住,__init__ 是你与对象进行第一次交互的窗口,是赋予对象"生命"和初始特征的关键步骤。下一课我们将继续探索类的其他方法类型。