深入解析:Python 类基础详解
️ Python类基础详解 - 从入门到精通
目录
- 1. 什么是类
- 2. 创建第一个类
- 3. 构造函数__init__
- 4. 实例属性和方法
- 5. 类属性和类方法
- 6. 静态方法
- 7. 属性访问控制
- 8. 实际应用示例
- 9. 最佳实践
1. 什么是类
1.1 基本概念
**类(Class)**是Python中面向对象编程的核心概念,它是创建对象的蓝图或模板。
简单理解:
类就像是一个表格模板
比如学生登记表模板:
姓名: _______
年龄: _______
学号: _______
对象就是根据这个模板填写的具体表格
- 张三的登记表 = 对象1
- 李四的登记表 = 对象2
- 王五的登记表 = 对象3
每个表格都有相同的栏目,但填写的内容不同
张三的表格:姓名=张三, 年龄=20, 学号=2024001
李四的表格:姓名=李四, 年龄=19, 学号=2024002
王五的表格:姓名=王五, 年龄=21, 学号=2024003
1.2 生活中的例子
# 想象一下学生登记表
# 学生登记表模板 = 类(Student)
# 根据模板填写的表格 = 对象(student1, student2, student3)
# 所有表格都有:姓名、年龄、学号
# 但每张表格的具体内容不同:
# student1: 姓名="张三", 年龄=20, 学号="2024001"
# student2: 姓名="李四", 年龄=19, 学号="2024002"
# student3: 姓名="王五", 年龄=21, 学号="2024003"
2. 创建第一个类
2.1 最简单的类
# 创建一个空的类
class Person:
"""人类 - 最简单的类定义"""
pass # pass表示什么都不做,只是占位符
# 创建对象(也叫实例)
person1 = Person() # 用Person类创建一个对象,叫person1
person2 = Person() # 用Person类创建另一个对象,叫person2
# 查看对象的类型和内容
print(type(person1)) # 显示person1的类型:<class '__main__.Person'>print(person1) # 显示person1的内容:<__main__.Person object at 0x...>
2.2 添加属性
class Person:
"""人类 - 添加属性"""
def __init__(self):
"""构造函数 - 创建对象时自动运行"""
self.name = "未知" # 给对象添加name属性,默认值是"未知"
self.age = 0 # 给对象添加age属性,默认值是0
self.gender = "未知" # 给对象添加gender属性,默认值是"未知"
# 创建对象
person1 = Person() # 创建第一个Person对象
person2 = Person() # 创建第二个Person对象
# 访问对象的属性(查看对象的信息)
print(f"姓名: {person1.name}") # 显示person1的姓名
print(f"年龄: {person1.age}") # 显示person1的年龄
print(f"性别: {person1.gender}") # 显示person1的性别
3. 构造函数__init__
3.1 什么是构造函数__init__?为什么要用它?
问题: 什么是构造函数__init__
?为什么要用它?
答案:__init__
是Python中的构造函数,它在创建对象时自动运行,用来初始化对象的属性。
用表格比喻理解:
__init__
就像制作表格时的自动填写程序- 每次制作新表格时,这个程序会自动运行
- 它会根据你提供的信息,自动填写表格的各个栏目
- 这样每张表格都有完整的信息,不会遗漏
为什么需要构造函数?
# 没有构造函数的问题
class BadStudent:
"""不好的学生类 - 没有构造函数"""
pass
# 创建对象
student = BadStudent()
# 问题1:对象是空的,没有任何属性
print(f"学生姓名: {student.name}") # 会报错!AttributeError
# 问题2:每次都要手动添加属性,很麻烦
student.name = "张三" # 手动添加姓名
student.age = 20 # 手动添加年龄
student.student_id = "2024001" # 手动添加学号
# 问题3:容易遗漏属性
# 如果忘记添加某个属性,程序就会出错
有构造函数的好处:
# 有构造函数的好处
class GoodStudent:
"""好的学生类 - 有构造函数"""
def __init__(self, name, age, student_id):
"""构造函数 - 自动初始化学生信息"""
print(f"正在创建学生: {name}") # 显示创建过程
self.name = name # 自动设置姓名
self.age = age # 自动设置年龄
self.student_id = student_id # 自动设置学号
self.grades = [] # 自动创建成绩列表
print(f"学生 {name} 创建完成") # 显示创建完成
# 创建对象(自动调用__init__)
student = GoodStudent("张三", 20, "2024001")
# 好处1:对象创建时就有完整属性
print(f"学生姓名: {student.name}") # 正常显示
print(f"学生年龄: {student.age}") # 正常显示
print(f"学生学号: {student.student_id}") # 正常显示
# 好处2:不会遗漏任何属性
print(f"成绩列表: {student.grades}") # 正常显示
运行结果:
正在创建学生: 张三
学生 张三 创建完成
学生姓名: 张三
学生年龄: 20
学生学号: 2024001
成绩列表: []
总结:
- ❌ 没有构造函数:对象是空的,容易出错,使用麻烦
- ✅ 有构造函数:对象创建时就有完整属性,使用方便,不容易出错
3.2 为什么要初始化赋值?
问题: 为什么不能直接给对象添加属性,而要在__init__
中初始化?
答案: 因为对象刚创建时是"空的",没有任何属性。我们需要给它添加属性并设置初始值。
# 错误的方式:不初始化赋值
class BadStudent:
"""不好的学生类 - 没有初始化"""
pass
# 创建对象
student = BadStudent()
# 尝试访问属性(会报错!)
try:
print(student.name) # 会报错:AttributeError
except AttributeError as e:
print(f"错误: {e}")
# 正确的方式:初始化赋值
class GoodStudent:
"""好的学生类 - 有初始化"""
def __init__(self, name, age):
"""初始化学生属性"""
self.name = name # 给对象添加name属性
self.age = age # 给对象添加age属性
# 创建对象
student = GoodStudent("张三", 20)
# 现在可以安全访问属性
print(f"姓名: {student.name}") # 正常显示:姓名: 张三
print(f"年龄: {student.age}") # 正常显示:年龄: 20
总结:
- ❌ 不初始化:对象是空的,访问属性会报错
- ✅ 初始化赋值:对象有属性,可以安全使用
3.3 用表格比喻理解初始化赋值
想象一下制作学生登记表的过程:
# 方式1:制作空白表格(不初始化)
class EmptyTable:
"""空白表格 - 没有填写任何信息"""
pass
# 制作空白表格
table1 = EmptyTable()
# 尝试查看表格内容(会出错!)
try:
print(table1.name) # 表格上还没有姓名栏
except AttributeError:
print("表格上还没有姓名栏!")
# 方式2:制作填写好的表格(初始化赋值)
class FilledTable:
"""填写好的表格 - 已经填写了基本信息"""
def __init__(self, name, age, student_id):
"""制作表格时自动填写信息"""
self.name = name # 在姓名栏填写姓名
self.age = age # 在年龄栏填写年龄
self.student_id = student_id # 在学号栏填写学号
print(f"正在制作 {name} 的登记表...")
# 制作填写好的表格
table2 = FilledTable("张三", 20, "2024001")
# 现在可以安全查看表格内容
print(f"姓名栏: {table2.name}")
print(f"年龄栏: {table2.age}")
print(f"学号栏: {table2.student_id}")
运行结果:
表格上还没有姓名栏!
正在制作 张三 的登记表...
姓名栏: 张三
年龄栏: 20
学号栏: 2024001
比喻总结:
- 不初始化 = 制作空白表格,没有填写任何信息
- 初始化赋值 = 制作表格时自动填写基本信息
- 访问属性 = 查看表格上的具体信息
3.4 构造函数的详细示例
现在让我们看一个完整的构造函数示例:
class Student:
"""学生类 - 演示构造函数"""
def __init__(self, name, age, student_id):
"""构造函数 - 初始化学生信息
参数说明:
- name: 学生姓名
- age: 学生年龄
- student_id: 学生学号
"""
print(f"正在创建学生: {name}") # 显示创建过程
self.name = name # 把传入的name赋值给对象的name属性
self.age = age # 把传入的age赋值给对象的age属性
self.student_id = student_id # 把传入的student_id赋值给对象的student_id属性
self.grades = [] # 创建一个空的成绩列表
print(f"学生 {name} 创建完成") # 显示创建完成
# 创建学生对象(会自动调用__init__方法)
student1 = Student("张三", 20, "2024001") # 创建张三的学生对象
student2 = Student("李四", 19, "2024002") # 创建李四的学生对象
# 查看创建好的学生信息
print(f"学生1: {student1.name}, 年龄: {student1.age}")
print(f"学生2: {student2.name}, 年龄: {student2.age}")
3.5 默认参数
class Book:
"""图书类 - 演示默认参数"""
def __init__(self, title, author, pages=100, price=0.0):
"""构造函数 - 带默认参数
参数说明:
- title: 书名(必须提供)
- author: 作者(必须提供)
- pages: 页数(可选,默认100页)
- price: 价格(可选,默认0.0元)
"""
self.title = title # 设置书名
self.author = author # 设置作者
self.pages = pages # 设置页数
self.price = price # 设置价格
self.is_available = True # 设置是否可借阅
# 使用默认参数创建对象
book1 = Book("Python编程", "张三") # 只提供书名和作者,页数和价格用默认值
book2 = Book("Java编程", "李四", 200, 59.9) # 提供所有参数
# 显示图书信息
print(f"《{book1.title}》- {book1.author}, {book1.pages}页, {book1.price}元")
print(f"《{book2.title}》- {book2.author}, {book2.pages}页, {book2.price}元")
4. 实例属性和方法
4.1 什么是实例属性?
问题: 什么是实例属性?为什么要用实例属性?
答案: 实例属性是属于特定对象的属性,每个对象都有自己独立的属性值。
用表格比喻理解:
- 每张学生登记表都有自己的姓名、年龄、学号
- 张三的表格:姓名=张三, 年龄=20
- 李四的表格:姓名=李四, 年龄=19
- 两张表格的信息互不影响
4.2 实例属性的特点
实例属性有以下特点:
- 独立性:每个对象都有自己独立的属性值
- 可变性:可以修改对象的属性值
- 互不影响:修改一个对象的属性不会影响其他对象
4.3 实例属性的实践示例
现在让我们看一个具体的例子:
class Car:
"""汽车类 - 演示实例属性"""
def __init__(self, brand, model, color, price):
"""构造函数 - 初始化汽车属性"""
self.brand = brand # 汽车品牌(每个汽车对象都有自己的品牌)
self.model = model # 汽车型号(每个汽车对象都有自己的型号)
self.color = color # 汽车颜色(每个汽车对象都有自己的颜色)
self.price = price # 汽车价格(每个汽车对象都有自己的价格)
self.mileage = 0 # 里程数(新车的里程数都是0)
self.is_running = False # 是否运行中(新车默认是关闭状态)
# 创建汽车对象
car1 = Car("奔驰", "C200", "黑色", 350000) # 创建第一辆汽车
car2 = Car("宝马", "X3", "白色", 450000) # 创建第二辆汽车
# 每个对象都有独立的属性(互不影响)
print(f"汽车1: {car1.brand} {car1.model}, 颜色: {car1.color}")
print(f"汽车2: {car2.brand} {car2.model}, 颜色: {car2.color}")
# 修改一个对象的属性不会影响另一个对象
car1.mileage = 10000 # 给第一辆汽车设置里程数
print(f"汽车1里程: {car1.mileage}") # 显示第一辆汽车的里程数
print(f"汽车2里程: {car2.mileage}") # 第二辆汽车的里程数还是0
4.4 什么是实例方法?
问题: 什么是实例方法?为什么要用实例方法?
答案: 实例方法是定义在类中的函数,可以访问和修改实例属性。
用表格比喻理解:
- 实例方法就像表格上的操作按钮
- 可以查看表格内容(读取属性)
- 可以修改表格内容(修改属性)
- 可以执行表格相关的操作
4.5 实例方法的实践示例
现在让我们看一个具体的例子:
class Car:
"""汽车类 - 演示实例方法"""
def __init__(self, brand, model, color, price):
"""构造函数 - 初始化汽车属性"""
self.brand = brand # 汽车品牌
self.model = model # 汽车型号
self.color = color # 汽车颜色
self.price = price # 汽车价格
self.mileage = 0 # 里程数(初始为0)
self.is_running = False # 引擎状态(初始为关闭)
def start_engine(self):
"""启动引擎方法"""
if not self.is_running: # 如果引擎没有运行
self.is_running = True # 设置引擎状态为运行
print(f"{self.brand} {self.model} 引擎已启动")
else: # 如果引擎已经在运行
print(f"{self.brand} {self.model} 引擎已经在运行")
def stop_engine(self):
"""关闭引擎方法"""
if self.is_running: # 如果引擎正在运行
self.is_running = False # 设置引擎状态为关闭
print(f"{self.brand} {self.model} 引擎已关闭")
else: # 如果引擎已经关闭
print(f"{self.brand} {self.model} 引擎已经关闭")
def drive(self, distance):
"""驾驶汽车方法
参数:
- distance: 要行驶的距离(公里)
"""
if self.is_running: # 如果引擎正在运行
self.mileage += distance # 增加里程数
print(f"{self.brand} {self.model} 行驶了{distance}公里")
print(f"总里程: {self.mileage}公里")
else: # 如果引擎没有运行
print("请先启动引擎")
def get_info(self):
"""获取汽车信息方法"""
status = "运行中" if self.is_running else "已关闭" # 根据引擎状态设置状态文字
return f"{self.brand} {self.model} ({self.color}) - {status} - 里程: {self.mileage}公里"
# 使用实例方法
car = Car("特斯拉", "Model 3", "蓝色", 300000) # 创建一辆特斯拉汽车
print(car.get_info()) # 显示汽车信息
car.start_engine() # 启动引擎
car.drive(50) # 行驶50公里
car.drive(30) # 再行驶30公里
car.stop_engine() # 关闭引擎
print(car.get_info()) # 再次显示汽车信息
5. 类属性和类方法
5.1 什么是类属性?
问题: 什么是类属性?为什么要用类属性?
答案: 类属性是属于类本身的属性,所有对象共享同一个类属性。
用表格比喻理解:
- 类属性就像表格模板的标题栏
- 所有表格都有相同的标题栏
- 修改标题栏会影响所有表格
- 例如:学校名称、表格编号等
5.2 类属性的特点
类属性有以下特点:
- 共享性:所有对象共享同一个类属性
- 全局性:修改类属性会影响所有对象
- 持久性:类属性在程序运行期间一直存在
5.3 类属性的实践示例
现在让我们看一个具体的例子:
class Car:
"""汽车类 - 演示类属性"""
# 类属性 - 所有实例共享
wheels = 4
engine_type = "内燃机"
manufacturer = "未知制造商"
def __init__(self, brand, model, color, price):
"""构造函数"""
self.brand = brand # 实例属性
self.model = model # 实例属性
self.color = color # 实例属性
self.price = price # 实例属性
def get_class_info(self):
"""获取类信息"""
return f"轮子数: {Car.wheels}, 引擎类型: {Car.engine_type}, 制造商: {Car.manufacturer}"
# 创建汽车对象
car1 = Car("奔驰", "C200", "黑色", 350000)
car2 = Car("宝马", "X3", "白色", 450000)
# 访问类属性
print(f"汽车轮子数: {Car.wheels}")
print(f"引擎类型: {Car.engine_type}")
# 通过实例访问类属性
print(car1.get_class_info())
print(car2.get_class_info())
# 修改类属性会影响所有实例
Car.wheels = 6
Car.engine_type = "电动"
print("修改类属性后:")
print(car1.get_class_info())
print(car2.get_class_info())
5.4 什么是类方法?为什么要用类方法?
问题: 为什么要有类方法?它和普通方法有什么区别?
答案: 类方法是专门用来操作类属性的方法,而不是操作对象属性的方法。
# 普通方法 vs 类方法的区别
class Student:
"""学生类 - 演示普通方法和类方法的区别"""
# 类属性 - 所有学生共享
school_name = "厦门工学院"
total_count = 0
def __init__(self, name, age):
"""构造函数"""
self.name = name # 实例属性 - 每个学生独有
self.age = age # 实例属性 - 每个学生独有
Student.total_count += 1 # 每创建一个学生,总数+1
# 普通方法(实例方法)- 操作对象属性
def get_student_info(self):
"""获取学生信息 - 普通方法"""
return f"学生: {self.name}, 年龄: {self.age}"
# 类方法 - 操作类属性
@classmethod
def get_school_info(cls):
"""获取学校信息 - 类方法"""
return f"学校: {cls.school_name}, 总学生数: {cls.total_count}"
@classmethod
def set_school_name(cls, new_name):
"""设置学校名称 - 类方法"""
cls.school_name = new_name
print(f"学校名称已更改为: {new_name}")
# 使用示例
student1 = Student("张三", 20)
student2 = Student("李四", 19)
# 使用普通方法(需要对象)
print(student1.get_student_info()) # 需要student1对象
print(student2.get_student_info()) # 需要student2对象
# 使用类方法(不需要对象,直接用类名)
print(Student.get_school_info()) # 直接用Student类名
Student.set_school_name("厦门大学") # 直接用Student类名
print(Student.get_school_info())
运行结果:
学生: 张三, 年龄: 20
学生: 李四, 年龄: 19
学校: 厦门工学院, 总学生数: 2
学校名称已更改为: 厦门大学
学校: 厦门大学, 总学生数: 2
总结对比:
方法类型 | 操作对象 | 调用方式 | 用途 |
---|---|---|---|
普通方法 | 对象属性 | 对象.方法() | 处理单个对象的信息 |
类方法 | 类属性 | 类名.方法() | 处理所有对象共享的信息 |
5.5 类方法的实践示例
类方法是使用@classmethod
装饰器定义的方法,第一个参数是类本身(通常命名为cls
)。
class Car:
"""汽车类 - 演示类方法"""
wheels = 4
manufacturer = "未知制造商"
total_cars = 0 # 总汽车数量
def __init__(self, brand, model, color, price):
"""构造函数"""
self.brand = brand
self.model = model
self.color = color
self.price = price
Car.total_cars += 1 # 每创建一个汽车,总数+1
@classmethod
def set_manufacturer(cls, manufacturer_name):
"""设置制造商 - 类方法"""
cls.manufacturer = manufacturer_name
print(f"制造商已设置为: {manufacturer_name}")
@classmethod
def get_total_cars(cls):
"""获取总汽车数量 - 类方法"""
return cls.total_cars
@classmethod
def create_tesla(cls, model, color, price):
"""创建特斯拉汽车 - 类方法"""
return cls("特斯拉", model, color, price)
def get_info(self):
"""获取汽车信息"""
return f"{self.brand} {self.model} ({self.color}) - {self.price}元"
# 使用类方法
print(f"总汽车数: {Car.get_total_cars()}")
# 创建汽车
car1 = Car("奔驰", "C200", "黑色", 350000)
car2 = Car("宝马", "X3", "白色", 450000)
print(f"总汽车数: {Car.get_total_cars()}")
# 设置制造商
Car.set_manufacturer("德国汽车集团")
# 使用类方法创建特定品牌的汽车
tesla = Car.create_tesla("Model S", "红色", 500000)
print(tesla.get_info())
6. 静态方法
6.1 什么是静态方法?为什么要用静态方法?
问题: 什么是静态方法?为什么要用静态方法?
答案: 静态方法是不需要访问类属性或对象属性的工具函数,它就像一个独立的计算器。
用表格比喻理解:
- 静态方法就像计算器
- 不需要看任何表格
- 只是用来计算
- 例如:计算年龄对应的天数、计算面积等
6.2 静态方法的特点
静态方法有以下特点:
- 独立性:不需要访问类属性或对象属性
- 工具性:只是用来执行特定的计算或操作
- 通用性:可以在任何地方调用,不依赖对象
6.3 静态方法的实践示例
现在让我们看一个具体的例子:
# 三种方法的对比
class Calculator:
"""计算器类 - 演示三种方法的区别"""
# 类属性
brand = "卡西欧"
def __init__(self):
"""构造函数"""
self.result = 0 # 实例属性
# 普通方法(实例方法)- 操作对象属性
def add(self, number):
"""加法 - 普通方法"""
self.result += number # 修改对象属性
return self.result
# 类方法 - 操作类属性
@classmethod
def get_brand(cls):
"""获取品牌 - 类方法"""
return cls.brand # 访问类属性
# 静态方法 - 不操作任何属性,只是工具函数
@staticmethod
def calculate_circle_area(radius):
"""计算圆面积 - 静态方法"""
import math
return math.pi * radius ** 2 # 不访问任何属性,只是计算
# 使用示例
calc = Calculator()
# 使用普通方法(需要对象)
print(calc.add(5)) # 需要calc对象
print(calc.add(3)) # 需要calc对象
# 使用类方法(不需要对象,但需要类)
print(Calculator.get_brand()) # 需要Calculator类
# 使用静态方法(不需要对象,也不需要类)
area = Calculator.calculate_circle_area(5) # 直接用类名调用
print(f"圆面积: {area:.2f}")
# 静态方法也可以这样调用(但通常不这样做)
calc2 = Calculator()
area2 = calc2.calculate_circle_area(3) # 通过对象调用(不推荐)
print(f"圆面积: {area2:.2f}")
运行结果:
5
8
卡西欧
圆面积: 78.54
圆面积: 28.27
总结对比:
方法类型 | 操作对象 | 调用方式 | 用途 | 是否需要对象 |
---|---|---|---|---|
普通方法 | 对象属性 | 对象.方法() | 处理单个对象的信息 | 需要 |
类方法 | 类属性 | 类名.方法() | 处理所有对象共享的信息 | 不需要 |
静态方法 | 无 | 类名.方法() | 工具函数,不依赖类或对象 | 不需要 |
6.4 三种方法的总结对比
用表格比喻来理解三种方法:
class StudentTable:
"""学生登记表类 - 演示三种方法"""
# 类属性 - 所有表格共享
school_name = "厦门工学院"
total_tables = 0
def __init__(self, name, age):
"""构造函数"""
self.name = name # 实例属性 - 每张表格独有
self.age = age # 实例属性 - 每张表格独有
StudentTable.total_tables += 1
# 普通方法 - 操作单张表格的内容
def get_student_info(self):
"""查看学生信息 - 普通方法"""
return f"姓名: {self.name}, 年龄: {self.age}"
# 类方法 - 操作所有表格的公共信息
@classmethod
def get_school_info(cls):
"""查看学校信息 - 类方法"""
return f"学校: {cls.school_name}, 总表格数: {cls.total_tables}"
# 静态方法 - 工具函数,不操作表格
@staticmethod
def calculate_age_in_days(age):
"""计算年龄对应的天数 - 静态方法"""
return age * 365 # 不操作任何表格,只是计算
# 使用示例
table1 = StudentTable("张三", 20)
table2 = StudentTable("李四", 19)
# 普通方法:查看单张表格的内容
print(table1.get_student_info()) # 查看张三的表格
print(table2.get_student_info()) # 查看李四的表格
# 类方法:查看所有表格的公共信息
print(StudentTable.get_school_info()) # 查看学校信息
# 静态方法:使用工具函数
days1 = StudentTable.calculate_age_in_days(20) # 计算20岁对应的天数
days2 = StudentTable.calculate_age_in_days(19) # 计算19岁对应的天数
print(f"张三的年龄相当于 {days1} 天")
print(f"李四的年龄相当于 {days2} 天")
运行结果:
姓名: 张三, 年龄: 20
姓名: 李四, 年龄: 19
学校: 厦门工学院, 总表格数: 2
张三的年龄相当于 7300 天
李四的年龄相当于 6935 天
比喻总结:
方法类型 | 表格比喻 | 操作对象 | 调用方式 |
---|---|---|---|
普通方法 | 查看单张表格的内容 | 对象属性 | 对象.方法() |
类方法 | 查看所有表格的公共信息 | 类属性 | 类名.方法() |
静态方法 | 使用计算器(工具) | 无 | 类名.方法() |
什么时候用哪种方法?
- 普通方法:需要处理单个对象的信息时
- 类方法:需要处理所有对象共享的信息时
- 静态方法:需要工具函数,不依赖对象或类时
class MathUtils:
"""数学工具类 - 演示静态方法"""
@staticmethod
def add(a, b):
"""加法"""
return a + b
@staticmethod
def multiply(a, b):
"""乘法"""
return a * b
@staticmethod
def is_even(number):
"""判断是否为偶数"""
return number % 2 == 0
@staticmethod
def calculate_circle_area(radius):
"""计算圆的面积"""
import math
return math.pi * radius ** 2
@staticmethod
def calculate_distance(x1, y1, x2, y2):
"""计算两点间距离"""
import math
return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
# 使用静态方法
print(f"5 + 3 = {MathUtils.add(5, 3)}")
print(f"4 * 6 = {MathUtils.multiply(4, 6)}")
print(f"8是偶数吗: {MathUtils.is_even(8)}")
print(f"半径为5的圆面积: {MathUtils.calculate_circle_area(5):.2f}")
print(f"两点间距离: {MathUtils.calculate_distance(0, 0, 3, 4):.2f}")
# 也可以通过实例调用静态方法
math_utils = MathUtils()
print(f"通过实例调用: {math_utils.add(10, 20)}")
7. 属性访问控制
7.1 什么是属性访问控制?
问题: 什么是属性访问控制?为什么要控制属性访问?
答案: 属性访问控制是控制外部代码如何访问和修改对象属性的机制。
用表格比喻理解:
- 有些表格信息是公开的(姓名、年龄)
- 有些表格信息是私有的(身份证号、家庭住址)
- 私有信息只能通过特定的方法查看和修改
7.2 公有属性
什么是公有属性?
公有属性是可以从外部直接访问和修改的属性。
class BankAccount:
"""银行账户类 - 演示公有属性"""
def __init__(self, account_holder, initial_balance=0):
"""构造函数"""
self.account_holder = account_holder # 公有属性
self.balance = initial_balance # 公有属性
self.account_number = "ACC" + str(id(self)) # 公有属性
# 创建账户
account = BankAccount("张三", 1000)
# 可以直接访问和修改公有属性
print(f"账户持有人: {account.account_holder}")
print(f"余额: {account.balance}")
print(f"账户号: {account.account_number}")
# 可以直接修改余额(不安全)
account.balance = 10000 # 危险!直接修改余额
print(f"修改后余额: {account.balance}")
7.2 受保护属性
使用单下划线_
前缀表示受保护属性,约定不应该从外部直接访问。
class BankAccount:
"""银行账户类 - 演示受保护属性"""
def __init__(self, account_holder, initial_balance=0):
"""构造函数"""
self.account_holder = account_holder
self._balance = initial_balance # 受保护属性
self._transaction_count = 0 # 受保护属性
def deposit(self, amount):
"""存款"""
if amount > 0:
self._balance += amount
self._transaction_count += 1
print(f"存款成功,余额: {self._balance}")
else:
print("存款金额必须大于0")
def withdraw(self, amount):
"""取款"""
if amount > 0:
if amount <= self._balance:
self._balance -= amount
self._transaction_count += 1
print(f"取款成功,余额: {self._balance}")
else:
print("余额不足")
else:
print("取款金额必须大于0")
def get_balance(self):
"""获取余额"""
return self._balance
def get_transaction_count(self):
"""获取交易次数"""
return self._transaction_count
# 使用受保护属性
account = BankAccount("李四", 2000)
# 推荐通过方法访问
account.deposit(500)
account.withdraw(200)
print(f"余额: {account.get_balance()}")
print(f"交易次数: {account.get_transaction_count()}")
# 仍然可以直接访问(但不推荐)
print(f"直接访问余额: {account._balance}")
7.3 私有属性
什么是私有属性?
私有属性是不能从外部直接访问的属性,只能通过类内部的方法访问。
用表格比喻理解:
- 私有属性就像表格中的机密信息
- 外部人员不能直接查看
- 只能通过特定的方法(如验证身份后)才能查看
class BankAccount:
"""银行账户类 - 演示私有属性"""
def __init__(self, account_holder, initial_balance=0):
"""构造函数"""
self.account_holder = account_holder
self.__balance = initial_balance # 私有属性
self.__transaction_history = [] # 私有属性
self.__pin = "1234" # 私有属性
def deposit(self, amount):
"""存款"""
if amount > 0:
self.__balance += amount
self.__transaction_history.append(f"存款: +{amount}")
print(f"存款成功,余额: {self.__balance}")
else:
print("存款金额必须大于0")
def withdraw(self, amount, pin):
"""取款"""
if pin != self.__pin:
print("密码错误")
return
if amount > 0:
if amount <= self.__balance:
self.__balance -= amount
self.__transaction_history.append(f"取款: -{amount}")
print(f"取款成功,余额: {self.__balance}")
else:
print("余额不足")
else:
print("取款金额必须大于0")
def get_balance(self):
"""获取余额"""
return self.__balance
def get_transaction_history(self):
"""获取交易历史"""
return self.__transaction_history.copy()
def change_pin(self, old_pin, new_pin):
"""修改密码"""
if old_pin == self.__pin:
self.__pin = new_pin
print("密码修改成功")
else:
print("原密码错误")
# 使用私有属性
account = BankAccount("王五", 3000)
# 正常操作
account.deposit(1000)
account.withdraw(500, "1234")
account.withdraw(100, "0000") # 密码错误
# 获取信息
print(f"余额: {account.get_balance()}")
print("交易历史:")
for transaction in account.get_transaction_history():
print(f" {transaction}")
# 尝试直接访问私有属性(会失败)
try:
print(account.__balance) # 会报错
except AttributeError as e:
print(f"无法访问私有属性: {e}")
# 修改密码
account.change_pin("1234", "5678")
account.withdraw(200, "5678")
8. 实际应用示例
8.1 学生成绩管理系统
现在让我们用学到的知识创建一个完整的学生成绩管理系统:
class Student:
"""学生类 - 成绩管理系统"""
# 类属性
school_name = "厦门工学院"
total_students = 0
def __init__(self, name, student_id, major):
"""构造函数"""
self.name = name
self.student_id = student_id
self.major = major
self.courses = {} # 课程成绩字典
self.gpa = 0.0
Student.total_students += 1
def add_course(self, course_name, grade):
"""添加课程成绩"""
if 0 <= grade <= 100:
self.courses[course_name] = grade
self._calculate_gpa()
print(f"{self.name}的{course_name}成绩已添加: {grade}")
else:
print("成绩必须在0-100之间")
def remove_course(self, course_name):
"""删除课程"""
if course_name in self.courses:
del self.courses[course_name]
self._calculate_gpa()
print(f"{self.name}的{course_name}课程已删除")
else:
print(f"{self.name}没有选修{course_name}")
def _calculate_gpa(self):
"""计算GPA(私有方法)"""
if self.courses:
self.gpa = sum(self.courses.values()) / len(self.courses)
else:
self.gpa = 0.0
def get_course_count(self):
"""获取课程数量"""
return len(self.courses)
def get_best_course(self):
"""获取最高分课程"""
if self.courses:
best_course = max(self.courses, key=self.courses.get)
return best_course, self.courses[best_course]
return None, 0
def get_worst_course(self):
"""获取最低分课程"""
if self.courses:
worst_course = min(self.courses, key=self.courses.get)
return worst_course, self.courses[worst_course]
return None, 0
def get_student_info(self):
"""获取学生信息"""
return f"""
学生信息:
姓名: {self.name}
学号: {self.student_id}
专业: {self.major}
学校: {Student.school_name}
课程数量: {self.get_course_count()}
GPA: {self.gpa:.2f}
"""
@classmethod
def get_school_info(cls):
"""获取学校信息"""
return f"学校: {cls.school_name}, 总学生数: {cls.total_students}"
@staticmethod
def grade_to_letter(grade):
"""成绩转换为等级"""
if grade >= 90:
return "A"
elif grade >= 80:
return "B"
elif grade >= 70:
return "C"
elif grade >= 60:
return "D"
else:
return "F"
# 使用学生管理系统
print("=== 学生成绩管理系统 ===")
# 创建学生
student1 = Student("张三", "2024001", "计算机科学")
student2 = Student("李四", "2024002", "软件工程")
# 添加课程成绩
student1.add_course("Python编程", 85)
student1.add_course("数据结构", 92)
student1.add_course("算法设计", 78)
student1.add_course("数据库", 88)
student2.add_course("Java编程", 90)
student2.add_course("Web开发", 85)
student2.add_course("软件工程", 82)
# 显示学生信息
print(student1.get_student_info())
print(student2.get_student_info())
# 获取最高分和最低分课程
best_course, best_grade = student1.get_best_course()
worst_course, worst_grade = student1.get_worst_course()
print(f"{student1.name}最高分课程: {best_course} ({best_grade}分)")
print(f"{student1.name}最低分课程: {worst_course} ({worst_grade}分)")
# 成绩等级转换
print(f"85分对应等级: {Student.grade_to_letter(85)}")
print(f"78分对应等级: {Student.grade_to_letter(78)}")
# 学校信息
print(Student.get_school_info())
9. 最佳实践
9.1 命名规范
类名: 使用大驼峰命名法(PascalCase)
方法名: 使用小驼峰命名法(camelCase)或下划线命名法(snake_case)
属性名: 使用下划线命名法(snake_case)
class UserAccount:
"""用户账户类 - 遵循命名规范"""
# 类属性使用大写
MAX_LOGIN_ATTEMPTS = 3
DEFAULT_BALANCE = 0.0
def __init__(self, username, email):
# 实例属性使用小写
self.username = username
self.email = email
self._balance = UserAccount.DEFAULT_BALANCE # 受保护属性
self.__login_attempts = 0 # 私有属性
# 公共方法使用小写
def deposit(self, amount):
"""存款"""
pass
# 私有方法使用双下划线
def __validate_amount(self, amount):
"""验证金额"""
return amount > 0
# 属性访问器
@property
def balance(self):
"""余额属性"""
return self._balance
@balance.setter
def balance(self, value):
"""设置余额"""
if value >= 0:
self._balance = value
else:
raise ValueError("余额不能为负数")
9.2 类设计原则
单一职责原则:每个类只负责一个功能
开闭原则:对扩展开放,对修改关闭
里氏替换原则:子类可以替换父类
接口隔离原则:使用多个专门的接口,而不是一个总接口
class Student:
"""学生类 - 遵循单一职责原则"""
def __init__(self, name, age):
self.name = name
self.age = age
def get_info(self):
"""获取学生信息"""
return f"姓名: {self.name}, 年龄: {self.age}"
class StudentGradeManager:
"""学生成绩管理类 - 专门负责成绩管理"""
def __init__(self, student):
self.student = student
self.grades = {}
def add_grade(self, subject, grade):
"""添加成绩"""
self.grades[subject] = grade
def get_average(self):
"""计算平均分"""
if self.grades:
return sum(self.grades.values()) / len(self.grades)
return 0
# 使用示例
student = Student("张三", 20)
grade_manager = StudentGradeManager(student)
grade_manager.add_grade("数学", 85)
grade_manager.add_grade("英语", 90)
print(student.get_info())
print(f"平均分: {grade_manager.get_average()}")
10. 学习要点总结
10.1 核心概念回顾
类定义:使用class
关键字定义类,类名使用大驼峰命名
构造函数:__init__
方法用于初始化对象属性
实例属性:每个对象都有独立的属性值
实例方法:可以访问和修改实例属性的方法
类属性:所有实例共享的属性
类方法:使用@classmethod
装饰器,操作类属性
静态方法:使用@staticmethod
装饰器,不依赖类或实例
访问控制:使用_
和__
控制属性访问级别
10.2 练习建议
练习建议:
- 创建一个
Rectangle
类,计算面积和周长 - 设计一个
BankAccount
类,实现存款、取款功能 - 实现一个
Library
类,管理图书借阅 - 创建一个
Employee
类,计算工资和奖金
厦门工学院人工智能创作坊 --郑恩赐
2025年9月29日
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/924089.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!