Python 构造函数
什么是构造函数?
在 Python 面向对象编程中,构造函数是一种特殊的方法,当创建类的新实例(对象)时自动调用。Python 中的构造函数名称固定为 __init__
(前后各有两个下划线)。构造函数主要用于设置新创建对象的初始状态,通常是为对象的属性赋予初始值。
__init__
方法并不是严格意义上的构造函数,因为对象在这个方法调用前已经被创建。更准确地说,它是一个初始化方法。但在 Python 社区中,通常将其称为"构造函数"。
构造函数的基本语法
class ClassName:
def __init__(self, parameters):
# 初始化代码
self.attribute1 = value1
self.attribute2 = value2
# 更多属性...
其中:
self
是一个指向当前实例的引用,必须是第一个参数parameters
是创建对象时传递的参数
简单构造函数示例
让我们通过一个简单的例子来理解构造函数的工作原理:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
return f"你好,我是 {self.name},我今年 {self.age} 岁。"
# 创建 Person 类的实例
person1 = Person("小明", 25)
person2 = Person("小红", 22)
# 调用实例方法
print(person1.introduce())
print(person2.introduce())
输出结果:
你好,我是 小明,我今年 25 岁。
你好,我是 小红,我今年 22 岁。
在上面的例子中:
- 我们定义了一个
Person
类,它有一个构造函数__init__
,接收name
和age
参数 - 构造函数将这些参数存储为实例的属性
- 当我们创建
Person
类的新实例时,构造函数自动运行,设置实例的初始状态
构造函数中的默认参数
与普通函数一样,构造函数也可以有默认参数:
class Book:
def __init__(self, title, author, pages=200, published=False):
self.title = title
self.author = author
self.pages = pages
self.published = published
def get_info(self):
publish_status = "已出版" if self.published else "未出版"
return f"《{self.title}》由{self.author}编著,共{self.pages}页,{publish_status}。"
# 使用不同参数组合创建实例
book1 = Book("Python入门", "张三")
book2 = Book("数据结构", "李四", 350)
book3 = Book("算法导论", "王五", 500, True)
print(book1.get_info())
print(book2.get_info())
print(book3.get_info())
输出结果:
《Python入门》由张三编著,共200页,未出版。
《数据结构》由李四编著,共350页,未出版。
《算法导论》由王五编著,共500页,已出版。
私有属性和构造函数
在 Python 中,以双下划线开头的属性被视为私有属性,不应从类外部直接访问。我们可以在构造函数中创建私有属性:
class BankAccount:
def __init__(self, account_number, owner_name, balance=0):
self.account_number = account_number
self.owner_name = owner_name
self.__balance = balance # 私有属性
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return f"存款 {amount} 元成功,当前余额: {self.__balance} 元"
return "存款金额必须大于零"
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
return f"取款 {amount} 元成功,当前余额: {self.__balance} 元"
return "余额不足或取款金额无效"
def get_balance(self):
return f"{self.owner_name} 的当前余额: {self.__balance} 元"
# 创建银行账户并操作
account = BankAccount("1001", "张三", 1000)
print(account.get_balance())
print(account.deposit(500))
print(account.withdraw(200))
print(account.get_balance())
# 尝试直接访问私有属性
try:
print(account.__balance)
except AttributeError as e:
print("无法直接访问私有属性")
输出结果:
张三 的当前余额: 1000 元
存款 500 元成功,当前余额: 1500 元
取款 200 元成功,当前余额: 1300 元
张三 的当前余额: 1300 元
无法直接访问私有属性
虽然 Python 中以双下划线开头的属性会被名称改写(名称修饰),使其在类外部难以直接访问,但这只是一种约定,而非强制的访问控制。实际上,仍可以通过 _ClassName__attribute
的方式访问这些"私有"属性。
在构造函数中调用其他方法
构造函数可以调用类中定义的其他方法:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
self.area = self.calculate_area() # 在构造函数中调用方法
def calculate_area(self):
return self.width * self.height
def calculate_perimeter(self):
return 2 * (self.width + self.height)
def display_info(self):
return f"矩形: 宽度={self.width}, 高度={self.height}, 面积={self.area}"
rect = Rectangle(5, 3)
print(rect.display_info())
print(f"周长: {rect.calculate_perimeter()}")
输出结果:
矩形: 宽度=5, 高度=3, 面积=15
周长: 16
实际应用案例:创建游戏角色类
下面是一个更复杂的实际例子,展示如何使用构造函数创建游戏角色类:
import random
class GameCharacter:
def __init__(self, name, character_class, level=1):
self.name = name
self.character_class = character_class
self.level = level
self.health = self.calculate_health()
self.power = self.calculate_power()
self.__experience = 0
self.__max_experience = 100 * self.level
def calculate_health(self):
base_health = {
"战士": 150,
"法师": 100,
"猎人": 120,
"牧师": 110
}
return base_health.get(self.character_class, 100) * self.level
def calculate_power(self):
base_power = {
"战士": 10,
"法师": 15,
"猎人": 12,
"牧师": 8
}
return base_power.get(self.character_class, 10) * self.level
def attack(self):
variation = random.uniform(0.8, 1.2)
damage = round(self.power * variation)
return f"{self.name} 发动攻击,造成 {damage} 点伤害!"
def gain_experience(self, amount):
self.__experience += amount
message = f"{self.name} 获得 {amount} 经验值。"
# 检查是否可以升级
if self.__experience >= self.__max_experience:
self.level_up()
message += f" {self.name} 升级了!当前等级: {self.level}"
return message
def level_up(self):
self.level += 1
self.__experience = self.__experience - self.__max_experience
self.__max_experience = 100 * self.level
self.health = self.calculate_health()
self.power = self.calculate_power()
def get_status(self):
return (f"角色: {self.name}\n"
f"职业: {self.character_class}\n"
f"等级: {self.level}\n"
f"生命值: {self.health}\n"
f"攻击力: {self.power}\n"
f"经验值: {self.__experience}/{self.__max_experience}")
# 创建不同角色
warrior = GameCharacter("亚瑟", "战士")
mage = GameCharacter("安琪拉", "法师", 2)
print(warrior.get_status())
print("\n" + mage.get_status())
# 模拟战斗
print("\n--- 战斗开始 ---")
print(warrior.attack())
print(mage.attack())
print(warrior.gain_experience(85))
print(mage.gain_experience(210)) # 这将触发升级
print("\n战斗后状态:")
print(warrior.get_status())
print("\n" + mage.get_status())
输出结果(由于随机因素,具体数值可能不同):
角色: 亚瑟
职业: 战士
等级: 1
生命值: 150
攻击力: 10
经验值: 0/100
角色: 安琪拉
职业: 法师
等级: 2
生命值: 200
攻击力: 30
经验值: 0/200
--- 战斗开始 ---
亚瑟 发动攻击,造成 11 点伤害!
安琪拉 发动攻击,造成 27 点伤害!
亚瑟 获得 85 经验值。
安琪拉 获得 210 经验值。 安琪拉 升级了!当前等级: 3
战斗后状态:
角色: 亚瑟
职业: 战士
等级: 1
生命值: 150
攻击力: 10
经验值: 85/100
角色: 安琪拉
职业: 法师
等级: 3
生命值: 300
攻击力: 45
经验值: 10/300
何时使用构造函数?
以下情况应该考虑使用构造函数:
- 当需要确保对象在创建时即处于有效且一致的状态
- 当对象需要接受初始参数来设置其状态
- 当需要在对象创建时执行一些必要的初始化操作
- 当需要对传入参数进行验证或转换后再存储为对象属性
构造函数最佳实践
- 保持简洁:构造函数应该只做必要的初始化工作,复杂逻辑应放在专用方法中
- 提供默认值:为常用参数提供合理的默认值,简化对象创建
- 参数验证:验证传入的参数是否有效,必要时抛出异常
- 避免过度依赖:构造函数不应依赖于太多外部条件
class Temperature:
def __init__(self, celsius=None, fahrenheit=None):
# 参数验证和逻辑处理
if celsius is not None and fahrenheit is not None:
raise ValueError("只能提供摄氏度或华氏度中的一个")
elif celsius is not None:
self.__celsius = float(celsius)
elif fahrenheit is not None:
self.__celsius = (float(fahrenheit) - 32) * 5/9
else:
self.__celsius = 0.0 # 默认值
def get_celsius(self):
return self.__celsius
def get_fahrenheit(self):
return (self.__celsius * 9/5) + 32
# 使用不同方式创建对象
temp1 = Temperature(celsius=25)
temp2 = Temperature(fahrenheit=77)
temp3 = Temperature() # 使用默认值
print(f"温度1: {temp1.get_celsius()}°C = {temp1.get_fahrenheit()}°F")
print(f"温度2: {temp2.get_celsius()}°C = {temp2.get_fahrenheit()}°F")
print(f"温度3: {temp3.get_celsius()}°C = {temp3.get_fahrenheit()}°F")
try:
invalid_temp = Temperature(celsius=30, fahrenheit=86)
except ValueError as e:
print(f"错误: {e}")
输出结果:
温度1: 25.0°C = 77.0°F
温度2: 25.0°C = 77.0°F
温度3: 0.0°C = 32.0°F
错误: 只能提供摄氏度或华氏度中的一个
总结
Python 构造函数 __init__
是面向对象编程中非常重要的概念,它允许我们:
- 为新创建的对象设置初始状态
- 接受参数并将其转换为对象的属性
- 执行必要的初始化逻辑
- 确保对象创建时处于有效状态
掌握构造函数的使用是成为熟练 Python 程序员的基础。理解构造函数如何工作以及如何有效使用它们,将帮助你设计出更加健壮和可维护的面向对象代码。
练习题
-
创建一个
Student
类,构造函数接收name
、age
和courses
(课程列表,默认为空列表)参数。添加方法来添加或删除课程,并显示学生信息。 -
设计一个
Car
类,构造函数接收品牌、型号、年份和里程数。包含私有属性__mileage
,并提供方法来更新里程数(确保新里程数不能小于当前里程数)。 -
创建一个
ShoppingCart
类,构造函数初始化一个空的购物车。添加方法来添加商品、移除商品、计算总价,并显示购物车内容。 -
挑战题:设计一个
Library
类来管理图书馆的书籍,构造函数接收一个可选的初始书籍字典。实现借书、还书和查找书籍的功能。