系统性地讲解Python类(class)和构造函数的相关知识。这是Python面向对象编程的核心内容。
1. 基础类定义
最简单的类
PYTHON
class MyClass:
"""这是一个简单的类"""
pass
# 创建实例
obj = MyClass()
print(obj) # <__main__.MyClass object at 0x...>
2. 构造函数详解
构造函数是创建对象时自动调用的方法,在Python中是 __init__ 方法。
2.1 默认构造函数
PYTHON
class Person:
def __init__(self):
"""默认构造函数,没有额外参数"""
self.name = "无名氏"
self.age = 0
print("默认构造函数被调用")
# 使用
p1 = Person()
print(p1.name) # 无名氏
print(p1.age) # 0
2.2 带参构造函数
所有参数都是必须的
PYTHON
class Person:
def __init__(self, name, age):
"""带参数的构造函数,所有参数都是必须的"""
self.name = name
self.age = age
print("带参构造函数被调用")
# 使用
p2 = Person("张三", 25)
print(p2.name, p2.age) # 张三 25
# 错误示例:缺少参数会报错
# p3 = Person() # TypeError
# p4 = Person("李四") # TypeError
2.3 混合参数构造函数(可选参数)
使用默认值创建可选参数
PYTHON
class Person:
def __init__(self, name, age=18, city="北京"):
"""
混合参数的构造函数
name: 必须参数
age: 可选参数,默认18
city: 可选参数,默认"北京"
"""
self.name = name
self.age = age
self.city = city
print("混合参数构造函数被调用")
# 使用方式1:提供所有参数
p1 = Person("张三", 25, "上海")
print(p1.name, p1.age, p1.city)
# 使用方式2:只提供必须参数
p2 = Person("李四")
print(p2.name, p2.age, p2.city) # 李四 18 北京
# 使用方式3:提供部分可选参数
p3 = Person("王五", 30)
print(p3.name, p3.age, p3.city) # 王五 30 北京
# 使用方式4:使用关键字参数
p4 = Person(name="赵六", city="广州")
print(p4.name, p4.age, p4.city) # 赵六 18 广州
更灵活的参数组合
PYTHON
class Employee:
def __init__(self, name, salary, department="技术部",
position="工程师", email=None):
"""
name: 必须参数
salary: 必须参数
department: 可选参数,默认"技术部"
position: 可选参数,默认"工程师"
email: 可选参数,默认None
"""
self.name = name
self.salary = salary
self.department = department
self.position = position
self.email = email if email else f"{name}@company.com"
def display_info(self):
info = f"""
=== 员工信息 ===
姓名: {self.name}
部门: {self.department}
职位: {self.position}
邮箱: {self.email}
薪资: {self.salary}
"""
print(info)
# 使用
emp1 = Employee("张三", 8000)
emp1.display_info()
emp2 = Employee("李四", 10000, "市场部", "经理", "lisi@company.com")
emp2.display_info()
3. 完整示例:学生管理系统
PYTHON
class Student:
# 类属性:所有实例共享
school = "XX大学"
total_students = 0
def __init__(self, student_id, name, age, major=None,
grade_points=0.0, is_active=True):
"""
student_id: 学号(必须)
name: 姓名(必须)
age: 年龄(必须)
major: 专业(可选,默认None)
grade_points: 绩点(可选,默认0.0)
is_active: 是否在读(可选,默认True)
"""
self.student_id = student_id
self.name = name
self.age = age
self.major = major if major else "未定专业"
self.grade_points = grade_points
self.is_active = is_active
# 每个实例创建时增加总学生数
Student.total_students += 1
def update_grade(self, new_grade):
"""更新绩点"""
self.grade_points = new_grade
print(f"{self.name}的绩点已更新为: {new_grade}")
def change_major(self, new_major):
"""更换专业"""
old_major = self.major
self.major = new_major
print(f"{self.name}的专业已从{old_major}更换为{new_major}")
def get_student_status(self):
"""获取学生状态"""
status = "在读" if self.is_active else "已毕业/休学"
return f"学号:{self.student_id} 姓名:{self.name} 专业:{self.major} 状态:{status}"
@classmethod
def get_total_students(cls):
"""类方法:获取学生总数"""
return f"学校总学生数: {cls.total_students}"
@staticmethod
def calculate_grade_level(grade_points):
"""静态方法:根据绩点判断等级"""
if grade_points >= 4.0:
return "优秀"
elif grade_points >= 3.0:
return "良好"
elif grade_points >= 2.0:
return "及格"
else:
return "不及格"
# 使用示例
# 创建学生实例
stu1 = Student("2023001", "小明", 20, "计算机科学", 3.8)
stu2 = Student("2023002", "小红", 21, grade_points=3.2)
stu3 = Student("2023003", "小刚", 22, "物理学") # 使用默认绩点0.0
# 打印学生信息
print(stu1.get_student_status())
print(stu2.get_student_status())
print(stu3.get_student_status())
# 调用实例方法
stu1.update_grade(4.0)
stu2.change_major("人工智能")
# 调用类方法
print(Student.get_total_students())
# 调用静态方法
print(f"小明的等级: {Student.calculate_grade_level(stu1.grade_points)}")
4. 进阶:更复杂的构造函数
4.1 使用 *args 接受任意数量的位置参数
PYTHON
class Product:
def __init__(self, name, price, *tags):
"""
name: 商品名称
price: 价格
*tags: 可变数量的标签
"""
self.name = name
self.price = price
self.tags = list(tags)
def add_tag(self, tag):
self.tags.append(tag)
def show_tags(self):
return f"{self.name}的标签: {', '.join(self.tags)}"
p1 = Product("手机", 2999, "电子产品", "通讯", "便携")
print(p1.show_tags()) # 手机的标签: 电子产品, 通讯, 便携
p2 = Product("笔记本", 4999, "办公", "学习")
print(p2.show_tags()) # 笔记本的标签: 办公, 学习
4.2 使用 **kwargs 接受任意数量的关键字参数
PYTHON
class Car:
def __init__(self, brand, model, **specifications):
"""
brand: 品牌
model: 型号
**specifications: 其他技术参数
"""
self.brand = brand
self.model = model
self.specifications = specifications
def get_full_info(self):
info = f"{self.brand} {self.model}\n规格参数:"
for key, value in self.specifications.items():
info += f"\n {key}: {value}"
return info
car1 = Car("特斯拉", "Model 3", color="红色", range=500, autopilot=True)
print(car1.get_full_info())
car2 = Car("比亚迪", "汉", color="黑色", battery_type="刀片电池",
acceleration=3.9, range=605)
print(car2.get_full_info())
4.3 混合使用默认参数、*args、**kwargs
python
<PYTHON>
class Config:
def __init__(self, name, default_value=None, *args, **kwargs):
"""
复杂的参数配置类
"""
self.name = name
self.default_value = default_value
self.args = args
self.options = kwargs
def display_config(self):
print(f"配置名称: {self.name}")
print(f"默认值: {self.default_value}")
print(f"额外参数: {self.args}")
print(f"选项: {self.options}")
# 使用
c1 = Config("数据库配置", "localhost:3306", "utf8", "pool=10",
timeout=30, max_connections=100)
c1.display_config()
5. 最佳实践建议
- 参数顺序:将必须参数放在前面,可选参数放在后面
- 默认值设置 :使用
None作为默认值,然后在构造函数内赋值 - 类型提示:使用类型提示提高代码可读性
- 文档字符串:为构造函数和方法添加文档字符串
- 参数验证:在构造函数中进行参数验证
PYTHON
from typing import Optional, List, Dict
class User:
def __init__(self,
username: str,
email: str,
age: int = 18,
roles: Optional[List[str]] = None,
preferences: Optional[Dict] = None):
"""
用户类
Args:
username: 用户名(必须)
email: 邮箱(必须)
age: 年龄(可选,默认18)
roles: 角色列表(可选)
preferences: 偏好设置(可选)
"""
# 参数验证
if not username:
raise ValueError("用户名不能为空")
# 'email' 简单验证
if '@' not in email:
raise ValueError("邮箱格式不正确")
self.username = username
self.email = email
self.age = age
# 处理可变默认参数
self.roles = roles if roles is not None else []
self.preferences = preferences if preferences is not None else {}
def __str__(self):
return f"User(username={self.username}, email={self.email}, age={self.age})"
# 使用
user1 = User("john_doe", "john@example.com")
user2 = User("jane_doe", "jane@example.com", 25, ["admin", "editor"])
6. 使用dataclass简化类定义(Python 3.7+)
PYTHON
from dataclasses import dataclass, field
from typing import List
@dataclass
class Product:
name: str
price: float
tags: List[str] = field(default_factory=list)
stock: int = 0
def is_available(self):
return self.stock > 0
# 自动生成 __init__, __repr__ 等方法
p1 = Product("手机", 2999.0, ["电子", "通讯"], 10)
print(p1) # Product(name='手机', price=2999.0, tags=['电子', '通讯'], stock=10)
print(p1.is_available()) # True
总结
- 构造函数 :使用
__init__方法定义 - 必须参数:放在参数列表最前面,调用时必须提供
- 可选参数 :使用
=默认值的形式定义 - 灵活参数 :使用
*args和**kwargs - 最佳实践:添加类型提示、参数验证和文档字符串
什么是 typing 库?
想象一下你有一个装零食的盒子:
- 没有类型注解:你只知道盒子里有东西,但不知道是什么
- 有类型注解:盒子上贴着标签:"糖果"、"饼干"、"巧克力"
typing 就是给代码"贴标签"的工具!
最简单的理解方式
1. List - "列表标签"
告诉别人这个列表里装的是什么
PYTHON
from typing import List
# 没有标签(不知道盒子里是什么)
names = ["小明", "小红", "小刚"]
# 有标签(明确告诉大家是"字符串列表")
names: List[str] = ["小明", "小红", "小刚"]
# ↑ 就像在盒子上写:"装的是名字(字符串)"
2. Dict - "字典标签"
告诉别人字典里的"钥匙"和"宝藏"是什么类型
PYTHON
from typing import Dict
# 学生成绩单
# 没有标签
scores = {"小明": 90, "小红": 95}
# 有标签(钥匙是名字,宝藏是分数)
scores: Dict[str, int] = {"小明": 90, "小红": 95}
# ↑ 标签上写着:钥匙是文字(str),宝藏是数字(int)
3. Optional - "也许有标签"
表示这个东西可能有,也可能没有
PYTHON
from typing import Optional
# 小明的生日
# 没有标签
birthday = None # 不知道生日
# 或者
birthday = "2000-01-01"
# 有标签(也许是日期,也许是空)
birthday: Optional[str] = None
# ↑ 标签上写着:可能是日期,也可能是"没有"
实际例子:班级管理
PYTHON
from typing import Optional, List, Dict
class Student:
"""学生类"""
def __init__(self, name: str, age: int):
self.name: str = name # 名字肯定是文字
self.age: int = age # 年龄肯定是数字
self.phone: Optional[str] = None # 电话可能有,可能没有
self.scores: Dict[str, int] = {} # 成绩单:科目->分数
self.friends: List[str] = [] # 朋友列表:都是名字
class Class:
"""班级类"""
def __init__(self, class_name: str):
# 班级数据
self.class_name: str = class_name
self.students: List[Student] = [] # 学生列表:都是Student对象
def add_student(self, student: Student) -> None:
"""添加学生"""
self.students.append(student)
def find_student(self, name: str) -> Optional[Student]:
"""查找学生"""
for student in self.students:
if student.name == name:
return student
return None # 没找到就返回None
def get_class_scores(self) -> Dict[str, Dict[str, int]]:
"""获取全班成绩"""
scores_dict = {}
for student in self.students:
scores_dict[student.name] = student.scores
return scores_dict
# 使用示例
if __name__ == "__main__":
# 创建一个班级
class_1 = Class("三年二班")
# 创建几个学生
xiaoming = Student("小明", 10)
xiaoming.phone = "13800138000" # 小明有电话
xiaoming.scores = {"数学": 95, "语文": 88}
xiaoming.friends = ["小红", "小刚"]
xiaohong = Student("小红", 9)
# 小红没有电话(phone是None)
xiaohong.scores = {"数学": 90, "语文": 92}
xiaohong.friends = ["小明"]
# 把学生加入班级
class_1.add_student(xiaoming)
class_1.add_student(xiaohong)
# 查找学生
found_student = class_1.find_student("小明")
if found_student:
print(f"找到了:{found_student.name}")
# 获取全班成绩
all_scores = class_1.get_class_scores()
print(f"全班成绩:{all_scores}")
更生活化的比喻
比喻1:超市购物清单
PYTHON
from typing import List, Dict
# 购物清单
# 没有类型注解
shopping_list = [
{"item": "苹果", "amount": 3, "price": 5.0},
{"item": "牛奶", "amount": 2, "price": 8.5}
]
# 有类型注解(清晰多了!)
shopping_list: List[Dict[str, any]] = [
{"item": "苹果", "amount": 3, "price": 5.0},
{"item": "牛奶", "amount": 2, "price": 8.5}
]
# ↑ 意思:这是一个列表,里面每项都是字典
比喻2:通讯录
PYTHON
from typing import List, Dict, Optional
# 通讯录里的联系人
class Contact:
def __init__(
self,
name: str, # 名字(肯定要有)
phone: str, # 电话(肯定要有)
email: Optional[str] = None, # 邮箱(可能有,可能没有)
groups: List[str] = None # 分组(列表)
):
self.name = name
self.phone = phone
self.email = email
self.groups = groups or []
def __str__(self) -> str: # 返回值是字符串
return f"{self.name}: {self.phone}"
# 通讯录
class AddressBook:
def __init__(self):
self.contacts: List[Contact] = [] # 联系人列表
def add_contact(self, contact: Contact) -> None:
self.contacts.append(contact)
def find_by_name(self, name: str) -> Optional[Contact]:
for contact in self.contacts:
if contact.name == name:
return contact
return None
# 使用
my_book = AddressBook()
alice = Contact("Alice", "123456789", "alice@email.com", ["朋友", "同事"])
bob = Contact("Bob", "987654321")
my_book.add_contact(alice)
my_book.add_contact(bob)
found = my_book.find_by_name("Alice")
if found:
print(found)
为什么要用 typing?
对新手的好处:
- 一看就懂:代码更清晰,就像加了注释
- 减少错误:编辑器会提醒你类型错误
- 学习更快:理解别人代码更容易
实际体验不同点:
不用 typing(糊涂状态) :
PYTHON
def process(data):
# 天呐!data是什么?列表?字典?数字?
# 只能猜或者看代码逻辑
return data * 2 # 这行代码对吗?不知道!
用 typing(清晰状态) :
PYTHON
def process(data: List[int]) -> List[int]:
# 噢!data是数字列表,返回值也是数字列表
# 所以 data * 2 可能不对(列表不能乘以2)
return [x * 2 for x in data] # 这样才对!