26·模块七:面向对象编程进阶

__init__ 方法与对象初始化

oopinitconstructor

第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 类。

  • 要求:
    1. __init__ 方法接收两个参数:title (书名) 和 author (作者)。
    2. __init__ 中将这两个参数分别赋值给实例属性 self.titleself.author
    3. 创建一个 Book 对象 my_book,书名为 "Python编程:从入门到实践",作者为 "Eric Matthes"。
    4. 打印出该书的信息,格式为:《[书名]》,作者:[作者]
  • 预期输出
    《Python编程:从入门到实践》,作者:Eric Matthes
    

练习题 2(进阶):改进 Book 类。

  • 要求:
    1. __init__ 增加一个可选参数 price,默认值为 29.9
    2. 添加一个实例属性 self.price 来存储价格。
    3. 添加一个实例属性 self.pages_read,初始化为 0
    4. 添加一个实例方法 read(pages),该方法将传入的 pages 数加到 self.pages_read 上。
    5. 创建对象并测试 read 方法。
  • 预期输出示例
    开始阅读,读了 50 页。
    目前已读页数:50
    

练习题 3(综合):设计一个 Student 类。

  • 要求:
    1. 初始化时需要传入 name (姓名) 和 student_id (学号)。
    2. 还有一个可选参数 courses,默认值为空列表 [],用于存储学生选修的课程列表。
    3. 提供一个方法 enroll(course),用于将课程名加入到学生的课程列表中(注意避免重复添加)。
    4. 提供一个方法 show_courses(),用于打印该学生的所有选修课。
    5. 创建一个学生对象,进行选课操作,并打印课程列表。
  • 预期输出示例
    学生 张三 (学号: S001) 选修了: ['数学', '物理', 'Python']
    

5. 常见错误

  1. 忘记 self 参数:定义 __init__ 或其他实例方法时,第一个参数必须是 self。忘记写会导致调用时参数数量不匹配的 TypeError
    # 错误示范
    class BadDog:
        def __init__(name, breed): # 错误!缺少 self
            self.name = name
    # my_dog = BadDog("Buddy") 会引发 TypeError
    
  2. __init__ 中误用 return__init__ 方法的作用是初始化对象,它不应该有返回值(除了默认的 None)。试图返回一个非 None 的值会导致 TypeError
    class BadClass:
        def __init__(self):
            return 42  # 错误!__init__ 不应返回值
    # obj = BadClass() 会引发 TypeError: __init__() should return None
    
  3. 混淆类变量和实例变量:在 __init__ 中定义的变量(使用 self.)是实例变量,每个对象一份。而直接在类体内(方法外)定义的变量是类变量,所有实例共享。
    class Counter:
        count = 0  # 这是类变量,所有实例共享
        def __init__(self):
            self.instance_id = id(self)  # 这是实例变量,每个对象不同
            Counter.count += 1  # 修改类变量,会影响所有实例
    
  4. 初始化逻辑过于复杂__init__ 应专注于设置初始状态。如果初始化过程非常复杂(如连接数据库、读取大文件),可能违反"单一职责原则"。可以考虑在 __init__ 中调用一个专门的初始化方法,或使用工厂模式。

6. 小结

本课我们深入学习了面向对象编程中至关重要的 __init__ 方法:

  • 核心作用__init__对象初始化方法,在对象创建后自动调用一次,用于设置对象的初始属性。
  • self 关键字self 代表当前正在被创建或操作的实例对象本身。通过 self,我们可以将数据绑定到这个具体的对象上。
  • 参数设计__init__ 的参数(除 self 外)就是在创建对象时 ClassName(arg1, arg2, ...) 中需要传入的参数。善用默认参数可以增加类的灵活性。
  • 实践意义:一个设计良好的 __init__ 能确保每个对象在诞生时就处于一个有效、一致且可用的状态,这是构建健壮、可预测程序的基础。

记住,__init__ 是你与对象进行第一次交互的窗口,是赋予对象"生命"和初始特征的关键步骤。下一课我们将继续探索类的其他方法类型。

练习编辑器

python
Loading...

继续学习

完成本课后,建议继续学习下一课「实例方法、类方法与静态方法」 以巩固所学知识。