文章目录
创作不易,请各位看官顺手点点关注,不胜感激 。
本篇将深入剖析类中一个至关重要的特殊方法:构造方法 __init__,以及它如何帮助我们创建拥有独特数据的实例属性。理解 __init__ 是掌握面向对象编程的关键一步,因为它决定了你创建的每个对象“长什么样”以及它“自带”了哪些初始数据。
1. 什么是构造方法 (__init__)?
在 Python 中,__init__(注意是两个下划线开头和结尾)是一个特殊的方法,被称为构造方法或初始化方法。它的作用是:
- 当一个类的新实例(对象)被创建时,
__init__方法会自动被调用。 - 它负责对新创建的实例进行初始化,即设置该实例特有的属性(数据)。
可以把 __init__ 想象成一个对象的“出生证明”或“配置器”。每当一个新对象诞生时,它都会根据 __init__ 的指示,获取它独有的特征。
1.1 __init__ 方法的语法
__init__ 方法的定义与普通方法类似,但有一些关键点:
class ClassName:
def __init__(self, parameter1, parameter2, ...):
# self 是必须的第一个参数,指向正在创建的实例本身
# self.attribute_name = parameter1 # 设置实例属性
# self.another_attribute = value # 可以在这里设置默认值
self: 这是__init__方法(以及所有实例方法)的第一个参数。它是一个约定俗成的名称,指代正在被创建或操作的当前实例(对象)本身。当你通过ClassName()创建一个对象时,Python 会自动将这个新创建的对象作为self参数传递给__init__。- 其他参数:
self之后的参数是你在创建对象时需要传入的值,这些值将用于初始化对象的实例属性。例如,创建一个Person对象可能需要name和age。
1.2 __init__ 的工作原理:幕后解析
让我们通过一个 Person 类的例子来理解 __init__ 的工作原理:
class Person:
def __init__(self, name, age):
"""
构造方法:在创建 Person 对象时自动调用,用于初始化实例属性。
Args:
name (str): 人的姓名。
age (int): 人的年龄。
"""
print(f"正在初始化一个名为 {name},年龄为 {age} 的 Person 对象...")
self.name = name # 将传入的 name 值赋给当前实例的 name 属性
self.age = age # 将传入的 age 值赋给当前实例的 age 属性
self.is_adult = (age >= 18) # 也可以根据参数计算并设置其他属性
def introduce(self):
"""介绍自己。"""
print(f"大家好,我叫 {self.name},我今年 {self.age} 岁了。")
if self.is_adult:
print("我是一名成年人。")
else:
print("我还是一名青少年。")
# 创建 Person 类的实例
print("--- 准备创建 person1 ---")
person1 = Person("Alice", 30) # ① Python 自动调用 __init__,将 person1 作为 self,"Alice" 作为 name,30 作为 age
print("--- person1 创建完成 ---")
person1.introduce()
print("\n--- 准备创建 person2 ---")
person2 = Person("Bob", 15) # ② 再次调用 __init__,将 person2 作为 self,"Bob" 作为 name,15 作为 age
print("--- person2 创建完成 ---")
person2.introduce()
输出:
--- 准备创建 person1 ---
正在初始化一个名为 Alice,年龄为 30 的 Person 对象...
--- person1 创建完成 ---
大家好,我叫 Alice,我今年 30 岁了。
我是一名成年人。
--- 准备创建 person2 ---
正在初始化一个名为 Bob,年龄为 15 的 Person 对象...
--- person2 创建完成 ---
大家好,我叫 Bob,我今年 15 岁了。
我还是一名青少年。
工作流程解析:
- 当你执行
person1 = Person("Alice", 30)时:- Python 首先在内存中为新的
Person对象分配空间。 - 然后,它自动调用
Person类中定义的__init__方法。 - 此时,
__init__方法内部的self参数会自动指向刚刚创建的那个新对象。name接收"Alice",age接收30。 self.name = name这一行代码的含义是:将传入的name(即"Alice") 赋值给当前对象 (self) 的一个名为name的属性。同理,self.age = age。__init__方法执行完毕后,这个已经被初始化了属性的新对象被赋值给了变量person1。
- Python 首先在内存中为新的
person1和person2是两个独立的实例,它们各自拥有自己的name、age和is_adult属性,互不影响。每次调用Person(...)都会触发__init__的执行,从而创建一个全新的、独立的实例。
2. 实例属性 (Instance Attributes)
通过 __init__ 方法,我们为每个对象设置了它独有的数据,这些数据就是实例属性。实例属性是每个对象特有的,它们的值可以不同。
例如,在 Person 类中:
self.name是一个实例属性。person1的name是 “Alice”,person2的name是 “Bob”。self.age是一个实例属性。person1的age是 30,person2的age是 15。self.is_adult也是一个实例属性,它的值根据age的不同而不同。
2.1 访问与修改实例属性
你可以随时使用点 . 运算符来访问或修改对象的实例属性:
# 访问实例属性
print(f"person1 的姓名: {person1.name}")
print(f"person2 的年龄: {person2.age}")
# 修改实例属性
person1.age = 31 # Alice 又过了一岁
print(f"person1 现在 {person1.age} 岁了。")
# 添加新的实例属性 (不推荐在 __init__ 之外随意添加,因为其他实例没有该属性)
person1.city = "New York"
print(f"person1 居住在: {person1.city}")
# print(person2.city) # 这会报错,因为 person2 没有 city 属性
输出:
person1 的姓名: Alice
person2 的年龄: 15
person1 现在 31 岁了。
person1 居住在: New York
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'Person' object has no attribute 'city'
重要提示: 虽然 Python 允许你在 __init__ 之外动态地为对象添加新属性,但强烈建议在 __init__ 中定义所有核心实例属性。这使得类的结构更清晰,每个对象创建后都有预期的属性集,便于代码维护和理解。在 __init__ 之外添加属性可能会导致其他实例没有该属性,从而引发 AttributeError。
2.2 实例属性与类属性回顾
再次强调 __init__ 中定义的实例属性与定义在类体中的类属性的区别:
| 特性 | 实例属性 (在 __init__ 中用 self.) | 类属性 (在类体中) |
|---|---|---|
| 归属 | 属于每个独立的对象(实例) | 属于类本身,所有实例共享 |
| 定义 | 通常在 __init__ 方法中通过 self.name = value 定义 | 在类体中,所有方法之外定义 name = value |
| 特点 | 每个实例都有自己的一份,值可以不同 | 所有实例共享同一份,值相同 |
| 访问 | 只能通过 instance_name.attribute_name 访问 | 通常通过 ClassName.attribute_name 访问,也可以通过 instance_name.attribute_name 访问 |
| 用途 | 存储对象特有的数据 | 存储类层面的常量或所有实例共有的默认值 |
| 修改 | 修改只影响当前实例 | 修改会影响所有实例(通过类名修改时) |
3. __init__ 与默认参数
__init__ 方法也可以使用我们之前学过的默认参数。这使得在创建对象时,某些属性可以有预设值,从而使实例化更加灵活。
class Book:
def __init__(self, title, author="佚名", pages=100): # author 和 pages 有默认值
self.title = title
self.author = author
self.pages = pages
self.is_read = False # 额外设置一个默认状态属性
def display_info(self):
read_status = "已读" if self.is_read else "未读"
print(f"《{self.title}》 - {self.author}, 共 {self.pages} 页, 状态: {read_status}")
# 创建实例时使用默认值
book1 = Book("Python 快速入门")
book1.display_info()
# 创建实例时覆盖默认值
book2 = Book("Effective Python", "Brett Slatkin", 300)
book2.display_info()
# 覆盖部分默认值 (使用关键字参数更清晰)
book3 = Book("算法导论", pages=1200) # 仅指定 title 和 pages,author 使用默认值
book3.display_info()
# 修改属性
book1.is_read = True
book1.display_info()
输出:
《Python 快速入门》 - 佚名, 共 100 页, 状态: 未读
《Effective Python》 - Brett Slatkin, 共 300 页, 状态: 未读
《算法导论》 - 佚名, 共 1200 页, 状态: 未读
《Python 快速入门》 - 佚名, 共 100 页, 状态: 已读
⚠️ 再次提醒: 如果你的 __init__ 方法中的默认参数是可变类型(如列表 [] 或字典 {}),请务必使用 None 作为默认值并在方法内部初始化,以避免所有实例共享同一个可变对象带来的陷阱。
class Student:
def __init__(self, name, courses=None): # 正确处理可变默认参数
self.name = name
self.courses = [] if courses is None else list(courses) # 避免共享可变对象
def add_course(self, course_name):
self.courses.append(course_name)
# 正确的用法
student1 = Student("Alice")
student1.add_course("Math")
print(f"{student1.name}'s courses: {student1.courses}") # ['Math']
student2 = Student("Bob") # 创建新实例时,会创建新的空列表
student2.add_course("Physics")
print(f"{student2.name}'s courses: {student2.courses}") # ['Physics']
print(f"Original Alice's courses: {student1.courses}") # ['Math'],Alice 的课程没有受到影响
总结
__init__ 方法是 Python 中类的核心,它定义了对象在创建之初所拥有的初始状态和数据。通过 __init__,你可以:
- 初始化实例属性:为每个对象设置其独有的数据。
- 确保数据完整性:在对象“诞生”时就具备所有必要的属性。
- 提高代码可读性:一眼就能看出创建该对象需要哪些参数以及它会拥有哪些初始特征。
self是__init__和所有实例方法的灵魂,它使得方法能够操作和访问当前实例的数据。
理解 __init__ 和实例属性,是深入理解面向对象编程的基石。
练习题
尝试独立完成以下练习题,并通过答案进行对照:
-
基础
Dog类:- 定义一个
Dog类。 - 在
__init__方法中,接收name和breed(品种)作为参数,并将它们保存为实例属性。 - 定义一个方法
bark(),打印"汪!我的名字是 [name]!"。 - 创建两只狗的对象,一只叫 “Buddy”,品种 “Golden Retriever”;另一只叫 “Lucy”,品种 “Labrador”。
- 分别让它们叫一声。
- 定义一个
-
Product类与默认参数:- 定义一个
Product类。 - 在
__init__方法中,接收name(产品名称),price(价格),quantity(数量,默认值为 1)作为参数,并保存为实例属性。 - 定义一个方法
get_total_cost(),返回产品的总价格(价格 * 数量)。 - 定义一个方法
display_product_info(),打印产品名称、价格、数量和总价格。 - 创建三个产品对象:
- “Laptop”,价格 1200,数量 1。
- “Mouse”,价格 25,数量 5。
- “Keyboard”,价格 75(使用默认数量)。
- 分别显示这些产品的信息。
- 定义一个
-
BankAccount类与状态管理:- 定义一个
BankAccount类。 - 在
__init__方法中,接收account_holder(持有人姓名)和initial_balance(初始余额,默认值为 0)作为参数。 - 保存
account_holder和balance为实例属性。 - 定义一个方法
deposit(amount),用于存款。确保amount大于 0。 - 定义一个方法
withdraw(amount),用于取款。确保amount大于 0 且不超过当前余额。 - 定义一个方法
get_balance(),返回当前余额。 - 创建两个银行账户对象,并进行存款、取款操作,然后打印余额。
- 定义一个
-
ShoppingCart类(使用可变默认参数的正确方式):- 定义一个
ShoppingCart类。 - 在
__init__方法中,接收customer_name作为参数,并初始化一个空的items列表作为实例属性。注意,这里items列表是实例特有的,不要用可变默认参数的陷阱方式。 - 定义一个方法
add_item(item_name, price, quantity=1),将商品添加到items列表中。每个商品可以存储为一个字典{'name': item_name, 'price': price, 'quantity': quantity}。 - 定义一个方法
get_total_items(),返回购物车中商品的总件数(所有商品的quantity之和)。 - 定义一个方法
get_total_cost(),返回购物车中所有商品的总价。 - 创建两个购物车对象,分别添加不同的商品,然后打印它们各自的总件数和总价。
- 定义一个
练习题答案
1. 基础 Dog 类:
# 1. 基础 Dog 类
class Dog:
def __init__(self, name, breed):
"""
初始化 Dog 对象。
Args:
name (str): 狗的名字。
breed (str): 狗的品种。
"""
self.name = name
self.breed = breed
def bark(self):
"""狗叫一声,并报上自己的名字。"""
print(f"汪!我的名字是 {self.name}!")
# 创建 Dog 对象
dog1 = Dog("Buddy", "Golden Retriever")
dog2 = Dog("Lucy", "Labrador")
# 让它们叫
dog1.bark()
dog2.bark()
# 访问属性
print(f"{dog1.name} 的品种是 {dog1.breed}")
print(f"{dog2.name} 的品种是 {dog2.breed}")
输出:
汪!我的名字是 Buddy!
汪!我的名字是 Lucy!
Buddy 的品种是 Golden Retriever
Lucy 的品种是 Labrador
2. Product 类与默认参数:
# 2. Product 类与默认参数
class Product:
def __init__(self, name, price, quantity=1):
"""
初始化 Product 对象。
Args:
name (str): 产品名称。
price (float/int): 产品价格。
quantity (int, optional): 产品数量。默认为 1。
"""
self.name = name
self.price = price
self.quantity = quantity
def get_total_cost(self):
"""返回产品的总价格。"""
return self.price * self.quantity
def display_product_info(self):
"""打印产品信息。"""
total_cost = self.get_total_cost()
print(f"--- 产品信息 ---")
print(f"名称: {self.name}")
print(f"价格: ${self.price:.2f}")
print(f"数量: {self.quantity}")
print(f"总价: ${total_cost:.2f}")
print("-" * 15)
# 创建产品对象
product1 = Product("Laptop", 1200) # 数量使用默认值 1
product2 = Product("Mouse", 25, 5)
product3 = Product("Keyboard", 75) # 数量使用默认值 1
# 显示产品信息
product1.display_product_info()
product2.display_product_info()
product3.display_product_info()
输出:
--- 产品信息 ---
名称: Laptop
价格: $1200.00
数量: 1
总价: $1200.00
---------------
--- 产品信息 ---
名称: Mouse
价格: $25.00
数量: 5
总价: $125.00
---------------
--- 产品信息 ---
名称: Keyboard
价格: $75.00
数量: 1
总价: $75.00
---------------
3. BankAccount 类与状态管理:
# 3. BankAccount 类与状态管理
class BankAccount:
def __init__(self, account_holder, initial_balance=0):
"""
初始化 BankAccount 对象。
Args:
account_holder (str): 账户持有人姓名。
initial_balance (float, optional): 初始余额。默认为 0。
"""
self.account_holder = account_holder
# 确保初始余额不为负
self.balance = max(0, float(initial_balance))
print(f"账户 '{self.account_holder}' 已创建,初始余额: ${self.balance:.2f}")
def deposit(self, amount):
"""
存款操作。
Args:
amount (float): 存款金额。
"""
if amount > 0:
self.balance += amount
print(f"存入 ${amount:.2f}。当前余额: ${self.balance:.2f}")
else:
print("存款金额必须大于零。")
def withdraw(self, amount):
"""
取款操作。
Args:
amount (float): 取款金额。
"""
if amount <= 0:
print("取款金额必须大于零。")
elif amount > self.balance:
print(f"余额不足。当前余额: ${self.balance:.2f},尝试取款: ${amount:.2f}")
else:
self.balance -= amount
print(f"取出 ${amount:.2f}。当前余额: ${self.balance:.2f}")
def get_balance(self):
"""返回当前余额。"""
return self.balance
# 创建银行账户对象
account1 = BankAccount("Alice Smith", 1000)
account2 = BankAccount("Bob Johnson") # 使用默认初始余额
print(f"\n{account1.account_holder} 账户操作:")
account1.deposit(200)
account1.withdraw(500)
account1.withdraw(1000) # 尝试超额取款
print(f"Alice 账户最终余额: ${account1.get_balance():.2f}")
print(f"\n{account2.account_holder} 账户操作:")
account2.deposit(50)
account2.deposit(100)
account2.withdraw(75)
print(f"Bob 账户最终余额: ${account2.get_balance():.2f}")
输出:
账户 'Alice Smith' 已创建,初始余额: $1000.00
账户 'Bob Johnson' 已创建,初始余额: $0.00
Alice Smith 账户操作:
存入 $200.00。当前余额: $1200.00
取出 $500.00。当前余额: $700.00
余额不足。当前余额: $700.00,尝试取款: $1000.00
Alice 账户最终余额: $700.00
Bob Johnson 账户操作:
存入 $50.00。当前余额: $50.00
存入 $100.00。当前余额: $150.00
取出 $75.00。当前余额: $75.00
Bob 账户最终余额: $75.00
4. ShoppingCart 类(使用可变默认参数的正确方式):
# 4. ShoppingCart 类 (使用可变默认参数的正确方式)
class ShoppingCart:
def __init__(self, customer_name):
"""
初始化 ShoppingCart 对象。
Args:
customer_name (str): 顾客姓名。
"""
self.customer_name = customer_name
self.items = [] # 每个购物车实例都有自己独立的空列表
def add_item(self, item_name, price, quantity=1):
"""
向购物车添加商品。
Args:
item_name (str): 商品名称。
price (float): 商品单价。
quantity (int, optional): 商品数量。默认为 1。
"""
if price > 0 and quantity > 0:
self.items.append({'name': item_name, 'price': price, 'quantity': quantity})
print(f"已将 {quantity} 个 '{item_name}' 添加到 {self.customer_name} 的购物车。")
else:
print(f"无法添加 '{item_name}': 价格和数量必须大于零。")
def get_total_items(self):
"""返回购物车中商品的总件数。"""
total_quantity = 0
for item in self.items:
total_quantity += item['quantity']
return total_quantity
def get_total_cost(self):
"""返回购物车中所有商品的总价。"""
total_price = 0
for item in self.items:
total_price += item['price'] * item['quantity']
return total_price
def display_cart_summary(self):
"""打印购物车摘要。"""
print(f"\n--- {self.customer_name} 的购物车摘要 ---")
if not self.items:
print("购物车为空。")
else:
for item in self.items:
print(f"- {item['name']} (单价: ${item['price']:.2f}, 数量: {item['quantity']})")
print(f"总件数: {self.get_total_items()}")
print(f"总价格: ${self.get_total_cost():.2f}")
print("-" * 30)
# 创建购物车对象
cart1 = ShoppingCart("Sarah")
cart2 = ShoppingCart("John")
# 为 Sarah 的购物车添加商品
cart1.add_item("Laptop", 1200)
cart1.add_item("Mouse", 25, 2)
cart1.add_item("Keyboard", 75)
# 为 John 的购物车添加商品
cart2.add_item("Headphones", 150)
cart2.add_item("USB Drive", 10, 3)
# 显示购物车摘要
cart1.display_cart_summary()
cart2.display_cart_summary()
# 验证两个购物车是独立的
print(f"\nSarah 的购物车商品列表: {cart1.items}")
print(f"John 的购物车商品列表: {cart2.items}")
输出:
已将 1 个 'Laptop' 添加到 Sarah 的购物车。
已将 2 个 'Mouse' 添加到 Sarah 的购物车。
已将 1 个 'Keyboard' 添加到 Sarah 的购物车。
已将 1 个 'Headphones' 添加到 John 的购物车。
已将 3 个 'USB Drive' 添加到 John 的购物车。
--- Sarah 的购物车摘要 ---
- Laptop (单价: $1200.00, 数量: 1)
- Mouse (单价: $25.00, 数量: 2)
- Keyboard (单价: $75.00, 数量: 1)
总件数: 4
总价格: $1325.00
------------------------------
--- John 的购物车摘要 ---
- Headphones (单价: $150.00, 数量: 1)
- USB Drive (单价: $10.00, 数量: 3)
总件数: 4
总价格: $180.00
------------------------------
Sarah 的购物车商品列表: [{'name': 'Laptop', 'price': 1200, 'quantity': 1}, {'name': 'Mouse', 'price': 25, 'quantity': 2}, {'name': 'Keyboard', 'price': 75, 'quantity': 1}]
John 的购物车商品列表: [{'name': 'Headphones', 'price': 150, 'quantity': 1}, {'name': 'USB Drive', 'price': 10, 'quantity': 3}]

被折叠的 条评论
为什么被折叠?



