跳到主要内容

Python 构造函数

什么是构造函数?

在 Python 面向对象编程中,构造函数是一种特殊的方法,当创建类的新实例(对象)时自动调用。Python 中的构造函数名称固定为 __init__(前后各有两个下划线)。构造函数主要用于设置新创建对象的初始状态,通常是为对象的属性赋予初始值。

备注

__init__ 方法并不是严格意义上的构造函数,因为对象在这个方法调用前已经被创建。更准确地说,它是一个初始化方法。但在 Python 社区中,通常将其称为"构造函数"。

构造函数的基本语法

python
class ClassName:
def __init__(self, parameters):
# 初始化代码
self.attribute1 = value1
self.attribute2 = value2
# 更多属性...

其中:

  • self 是一个指向当前实例的引用,必须是第一个参数
  • parameters 是创建对象时传递的参数

简单构造函数示例

让我们通过一个简单的例子来理解构造函数的工作原理:

python
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 岁。

在上面的例子中:

  1. 我们定义了一个 Person 类,它有一个构造函数 __init__,接收 nameage 参数
  2. 构造函数将这些参数存储为实例的属性
  3. 当我们创建 Person 类的新实例时,构造函数自动运行,设置实例的初始状态

构造函数中的默认参数

与普通函数一样,构造函数也可以有默认参数:

python
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 中,以双下划线开头的属性被视为私有属性,不应从类外部直接访问。我们可以在构造函数中创建私有属性:

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 的方式访问这些"私有"属性。

在构造函数中调用其他方法

构造函数可以调用类中定义的其他方法:

python
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

实际应用案例:创建游戏角色类

下面是一个更复杂的实际例子,展示如何使用构造函数创建游戏角色类:

python
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

何时使用构造函数?

以下情况应该考虑使用构造函数:

  1. 当需要确保对象在创建时即处于有效且一致的状态
  2. 当对象需要接受初始参数来设置其状态
  3. 当需要在对象创建时执行一些必要的初始化操作
  4. 当需要对传入参数进行验证或转换后再存储为对象属性

构造函数最佳实践

  1. 保持简洁:构造函数应该只做必要的初始化工作,复杂逻辑应放在专用方法中
  2. 提供默认值:为常用参数提供合理的默认值,简化对象创建
  3. 参数验证:验证传入的参数是否有效,必要时抛出异常
  4. 避免过度依赖:构造函数不应依赖于太多外部条件
python
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 程序员的基础。理解构造函数如何工作以及如何有效使用它们,将帮助你设计出更加健壮和可维护的面向对象代码。

练习题

  1. 创建一个 Student 类,构造函数接收 nameagecourses(课程列表,默认为空列表)参数。添加方法来添加或删除课程,并显示学生信息。

  2. 设计一个 Car 类,构造函数接收品牌、型号、年份和里程数。包含私有属性 __mileage,并提供方法来更新里程数(确保新里程数不能小于当前里程数)。

  3. 创建一个 ShoppingCart 类,构造函数初始化一个空的购物车。添加方法来添加商品、移除商品、计算总价,并显示购物车内容。

  4. 挑战题:设计一个 Library 类来管理图书馆的书籍,构造函数接收一个可选的初始书籍字典。实现借书、还书和查找书籍的功能。

额外资源