第53集:构造方法 __init__

学习目标

  • 理解__init__方法的作用和调用时机
  • 掌握__init__方法的定义和使用
  • 学会为对象初始化属性
  • 理解默认参数在__init__中的应用
  • 掌握对象创建的完整流程
  • 学会避免常见的初始化错误

一、什么是构造方法

1.1 构造方法的定义

构造方法:是一个特殊的方法,在创建对象时自动调用,用于初始化对象的属性。

在Python中,构造方法的名称是固定的:__init__

1.2 构造方法的作用

┌─────────────────────────────────────────┐
│           创建对象的过程                  │
├─────────────────────────────────────────┤
│  1. 分配内存空间                          │
│  2. 调用 __init__ 方法                   │
│     ↓                                    │
│     初始化对象的属性                      │
│  3. 返回对象引用                          │
└─────────────────────────────────────────┘

1.3 为什么需要构造方法

没有构造方法的问题:

class Person:
    def introduce(self):
        print(f"我叫{self.name},今年{self.age}岁")

p = Person()
# 需要手动设置属性
p.name = "张三"
p.age = 18
p.introduce()

有构造方法的好处:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def introduce(self):
        print(f"我叫{self.name},今年{self.age}岁")

# 创建对象时直接初始化
p = Person("张三", 18)
p.introduce()

二、__init__方法的基本语法

2.1 语法格式

class ClassName:
    def __init__(self, 参数1, 参数2, ...):
        # 初始化代码
        self.属性1 = 参数1
        self.属性2 = 参数2
        ...

2.2 基本示例

class Student:
    def __init__(self, name, age, grade):
        """初始化学生对象"""
        self.name = name
        self.age = age
        self.grade = grade

# 创建对象时,自动调用__init__方法
s = Student("张三", 18, "大一")
print(s.name)   # 输出:张三
print(s.age)    # 输出:18
print(s.grade)  # 输出:大一

2.3 参数传递机制

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# 创建对象时传入参数
p = Point(10, 20)

# 参数传递过程:
# Point(10, 20) -> __init__(self, 10, 20)
# self.x = 10, self.y = 20

三、__init__方法详解

3.1 self参数

self的含义:

  • self代表当前正在创建的对象
  • Python自动传递,不需要手动传递
class Demo:
    def __init__(self, value):
        print(f"self的值: {self}")
        print(f"self的类型: {type(self)}")
        self.value = value

d = Demo(100)
# 输出:
# self的值: <__main__.Demo object at 0x...>
# self的类型: <class '__main__.Demo'>

3.2 参数类型

3.2.1 必需参数

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 必须提供所有参数
p = Person("张三", 18)  # 正确
# p = Person("张三")    # 错误:缺少age参数

3.2.2 默认参数

class Person:
    def __init__(self, name, age=18):
        self.name = name
        self.age = age

# 可以只提供必需的参数
p1 = Person("张三")           # age使用默认值18
p2 = Person("李四", 20)       # age使用指定值20

print(f"{p1.name}: {p1.age}")  # 输出:张三: 18
print(f"{p2.name}: {p2.age}")  # 输出:李四: 20

3.2.3 可变参数

class Numbers:
    def __init__(self, *numbers):
        self.numbers = list(numbers)

n = Numbers(1, 2, 3, 4, 5)
print(n.numbers)  # 输出:[1, 2, 3, 4, 5]

3.2.4 关键字参数

class Person:
    def __init__(self, name, age, **info):
        self.name = name
        self.age = age
        self.info = info

p = Person("张三", 18, city="北京", job="程序员")
print(p.info)  # 输出:{'city': '北京', 'job': '程序员'}

四、__init__方法的使用场景

4.1 初始化基本属性

class Car:
    def __init__(self, brand, model, year, color):
        self.brand = brand
        self.model = model
        self.year = year
        self.color = color

car = Car("Toyota", "Camry", 2024, "白色")

4.2 初始化复杂数据结构

class Library:
    def __init__(self):
        self.books = []      # 书籍列表
        self.members = {}    # 成员字典
        self.borrowed = {}   # 借阅记录

library = Library()

4.3 初始化并计算

class Circle:
    pi = 3.14159
    
    def __init__(self, radius):
        self.radius = radius
        self.area = self.pi * radius ** 2  # 计算面积
        self.circumference = 2 * self.pi * radius  # 计算周长

circle = Circle(5)
print(circle.area)           # 输出:78.53975
print(circle.circumference)  # 输出:31.4159

4.4 参数验证

class AgeValidator:
    def __init__(self, age):
        if age < 0 or age > 150:
            raise ValueError("年龄必须在0-150之间")
        self.age = age

try:
    person = AgeValidator(200)  # 会报错
except ValueError as e:
    print(e)  # 输出:年龄必须在0-150之间

4.5 引用其他对象

class Engine:
    def __init__(self, power):
        self.power = power

class Car:
    def __init__(self, brand, engine_power):
        self.brand = brand
        self.engine = Engine(engine_power)  # 创建Engine对象

car = Car("Toyota", 200)
print(car.engine.power)  # 输出:200

五、默认参数的应用

5.1 简单默认参数

class Person:
    def __init__(self, name, age=18, gender="未知"):
        self.name = name
        self.age = age
        self.gender = gender

p1 = Person("张三")
p2 = Person("李四", 20)
p3 = Person("王五", 22, "男")

5.2 复杂默认参数

class BankAccount:
    def __init__(self, account_number, owner, balance=0, currency="CNY"):
        self.account_number = account_number
        self.owner = owner
        self.balance = balance
        self.currency = currency
        self.transactions = []  # 交易记录

account1 = BankAccount("001", "张三")
account2 = BankAccount("002", "李四", 1000)
account3 = BankAccount("003", "王五", 500, "USD")

5.3 使用None作为默认参数

class Person:
    def __init__(self, name, phone=None):
        self.name = name
        self.phone = phone if phone else "未设置"

p1 = Person("张三")
p2 = Person("李四", "13800138000")

print(p1.phone)  # 输出:未设置
print(p2.phone)  # 输出:13800138000

注意:避免使用可变对象作为默认参数

# 错误示例:使用列表作为默认参数
class BadExample:
    def __init__(self, items=[]):  # 错误!
        self.items = items

# 正确示例:使用None
class GoodExample:
    def __init__(self, items=None):
        if items is None:
            items = []
        self.items = items

六、对象创建的完整流程

6.1 详细流程图

创建对象: obj = ClassName(参数1, 参数2, ...)
    ↓
1. Python解释器分配内存空间
    ↓
2. 调用 __new__ 方法创建对象
    ↓
3. 调用 __init__ 方法初始化对象
    ↓
4. 返回对象引用
    ↓
5. 变量 obj 指向该对象

6.2 __new__ vs __init__

特性 __new__ __init__
作用 创建对象 初始化对象
参数 cls, *args, **kwargs self, *args, **kwargs
返回值 返回对象 不返回值(或返回None)
调用顺序 先调用 后调用
常用性 很少重写 经常重写

示例:

class Demo:
    def __new__(cls, *args, **kwargs):
        print("__new__被调用")
        return super().__new__(cls)
    
    def __init__(self, value):
        print("__init__被调用")
        self.value = value

d = Demo(100)
# 输出:
# __new__被调用
# __init__被调用

6.3 内存分配示意

内存空间
┌────────────────────────────┐
│  对象地址: 0x1000          │
│  ┌──────────────────────┐  │
│  │  name: "张三"        │  │
│  │  age: 18             │  │
│  │  ...其他属性         │  │
│  └──────────────────────┘  │
└────────────────────────────┘
         ↑
         │
    obj = Person("张三", 18)

七、综合示例

7.1 学生类完整示例

class Student:
    """学生类"""
    
    # 类属性
    school = "Python大学"
    total_students = 0
    
    def __init__(self, student_id, name, age, grade, scores=None):
        """初始化学生"""
        self.student_id = student_id
        self.name = name
        self.age = age
        self.grade = grade
        self.scores = scores if scores else {}
        Student.total_students += 1
    
    def add_score(self, subject, score):
        """添加成绩"""
        self.scores[subject] = score
    
    def get_average(self):
        """计算平均分"""
        if not self.scores:
            return 0
        return sum(self.scores.values()) / len(self.scores)

# 创建学生
s1 = Student("001", "张三", 18, "大一")
s2 = Student("002", "李四", 19, "大二", {"数学": 90, "英语": 85})

s1.add_score("Python", 88)
s1.add_score("数学", 92)

print(f"{s1.name}的平均分: {s1.get_average()}")
print(f"{s2.name}的平均分: {s2.get_average()}")

7.2 银行账户示例

class BankAccount:
    """银行账户类"""
    
    def __init__(self, account_number, owner, 
                 initial_balance=0, currency="CNY"):
        """初始化账户"""
        if initial_balance < 0:
            raise ValueError("初始余额不能为负数")
        
        self.account_number = account_number
        self.owner = owner
        self.balance = initial_balance
        self.currency = currency
        self.transactions = []
        
        if initial_balance > 0:
            self._record_transaction("初始存款", initial_balance)
    
    def _record_transaction(self, type_, amount):
        """记录交易(私有方法)"""
        self.transactions.append({
            "type": type_,
            "amount": amount,
            "balance": self.balance
        })
    
    def deposit(self, amount):
        """存款"""
        if amount <= 0:
            raise ValueError("存款金额必须大于0")
        self.balance += amount
        self._record_transaction("存款", amount)
    
    def withdraw(self, amount):
        """取款"""
        if amount <= 0:
            raise ValueError("取款金额必须大于0")
        if amount > self.balance:
            raise ValueError("余额不足")
        self.balance -= amount
        self._record_transaction("取款", -amount)

# 创建账户
account = BankAccount("6222000012345678", "张三", 1000)
account.deposit(500)
account.withdraw(200)

print(f"余额: {account.balance}")
print(f"交易记录: {account.transactions}")

八、常见错误与注意事项

8.1 常见错误

  1. 忘记self参数:
class Person:
    def __init__(name, age):  # 错误:缺少self
        self.name = name
        self.age = age
  1. 使用可变对象作为默认参数:
class Person:
    def __init__(self, name, hobbies=[]):  # 错误!
        self.name = name
        self.hobbies = hobbies
  1. 忘记初始化属性:
class Person:
    def __init__(self, name, age):
        self.name = name
        # 忘记初始化age
    
p = Person("张三", 18)
print(p.age)  # 报错:AttributeError
  1. 在__init__中返回值:
class Person:
    def __init__(self, name):
        self.name = name
        return self  # 错误:__init__不应该返回值

8.2 注意事项

  1. __init__方法名是固定的:必须是__init__,前后各两个下划线
  2. 第一个参数必须是self:代表当前对象
  3. 避免使用可变对象作为默认参数:使用None代替
  4. 可以进行参数验证:在__init__中验证参数的合法性
  5. 可以调用其他方法:在__init__中可以调用实例方法辅助初始化

九、课后练习

练习题

  1. 编程题

    • 定义一个Rectangle类,在__init__中初始化长和宽
    • 添加计算面积和周长的方法
    • 使用默认参数
  2. 编程题

    • 定义一个Book类,包含书名、作者、价格、出版年份
    • 使用关键字参数
    • 添加参数验证(价格必须大于0)
  3. 编程题

    • 定义一个GameCharacter
    • 包含姓名、生命值、攻击力
    • 生命值默认100,攻击力默认20
    • 添加方法验证生命值范围
  4. 思考题

    • __init__方法和普通方法有什么区别?
    • 为什么__init__方法需要self参数?
    • 什么时候应该在__init__中进行参数验证?

十、本集总结

核心知识点回顾

  1. __init__方法:构造方法,创建对象时自动调用
  2. 作用:初始化对象的属性
  3. 参数:第一个参数必须是self
  4. 默认参数:可以为参数设置默认值
  5. 参数验证:在__init__中验证参数的合法性
  6. 避免错误:不使用可变对象作为默认参数

下集预告

下一集我们将学习实例方法与self,深入了解如何使用self访问和操作对象的属性与方法。


学习建议: 熟练掌握__init__方法的使用,理解对象创建的完整过程。尝试编写不同类型的类,练习参数验证和默认参数的使用。

« 上一篇 类与对象基础 下一篇 » 实例方法与self