第6章 6.1 class 与命名空间
⚠️ 注意:本系列是 PHP 教程,但本章内容(class 与命名空间)在 Python 中概念相似,代码示例全部采用 Python 语法来演示核心思想,PHP 读者可轻松对应。
🎯 开场 3 分钟:为什么要学这个?
上一章我们用「博客系统后端」把函数、数组、文件操作串了一遍,代码越写越顺手。
但你可能遇到过这些崩溃时刻:
- 写了两个
process()函数,Python 报NameError: name 'process' is not defined - 接手别人的代码,3000 行堆在一个文件里,改一行坏三行
- 想用别人写好的代码,但不知道哪个文件里有哪些函数
这些问题,学完「类与命名空间」就能解决。
学完本文你能:
- 用 class 把数据和函数打包成「对象」
- 用「命名空间」给代码分区,避免名字冲突
- 读懂别人项目的结构,自己也能写出规范的代码
🧱 基础 25 分钟:核心概念
什么是 class?为什么\n\n
\n\n
\n\n要用?
生活类比:想象你要管理一群「学生」。
每个学生有:
- 属性:姓名、年龄、成绩
- 行为:上课、考试、交作业
不用 class 的话,你要写一堆散乱的变量和函数:
# 散乱写法
student1_name = "小明"
student1_age = 15
student1_score = 92
def student1_attend_class():
print("小明去上课")
def student1_take_exam():
print("小明考试得分:", student1_score)
用 class 打包后:
class Student:
def __init__(self, name, age, score):
self.name = name # 姓名
self.age = age # 年龄
self.score = score # 成绩
def attend_class(self):
print(f"{self.name}去上课")
def take_exam(self):
print(f"{self.name}考试得分: {self.score}")
# 创建一个小明
xiaoming = Student("小明", 15, 92)
xiaoming.attend_class() # 输出:小明去上课
一句话解释:class 就像一个「模板」,把相关的数据和行为打包在一起。
self 是什么?
class Student:
def __init__(self, name, age):
self.name = name # self.name 是「这个学生的名字」
self.age = age # self.age 是「这个学生的年龄」
生活类比:想象 self 是「我自己」。
当你喊「小明去上课」,self 就是那个被创建出来叫小明的具体对象。self.name 就是「我的名字」。
什么是命名空间?
生活类比:你家小区有两个「张伟」,怎么区分?
- 3号楼张伟
- 7号楼张伟
「3号楼」就是命名空间,把同名的人隔开了。
代码例子:
# 两个同名的 greet 函数
def greet():
print("你好,我是通用问候")
class Customer:
def greet(self):
print("你好,我是客户问候")
# 调用时必须指定是哪个
greet() # 输出:你好,我是通用问候
Customer().greet() # 输出:你好,我是客户问候
为什么要用:
- 避免名字冲突
- 让代码结构清晰,一眼看出这段代码属于哪个模块
模块与 import
Python 里每个 .py 文件就是一个「模块」,用 import 引入。
# math_utils.py 文件
def add(a, b):
return a + b
def multiply(a, b):
return a * b
# main.py 文件
import math_utils
result = math_utils.add(3, 5)
print(result) # 输出:8
另一种写法(给模块起别名):
import math_utils as mu
result = mu.add(3, 5)
print(result) # 输出:8
一句话解释:import 就像「从书架上拿一本书」,拿到才能用。
包(package)与目录结构
当项目大了,需要把模块分组管理。
my_project/
├── main.py
└── utils/
├── __init__.py # 空文件,表示这是个包
├── math_utils.py
└── string_utils.py
# main.py
from utils import math_utils
result = math_utils.add(10, 20)
print(result) # 输出:30
🔥 实战 35 分钟:3 个递进小项目
项目 1(5 分钟):学生信息管理
目标:用 class 管理 3 个学生的信息
class Student:
def __init__(self, name, age, grade):
self.name = name
self.age = age
self.grade = grade
def introduce(self):
print(f"我叫{self.name},今年{self.age}岁,读{self.grade}年级")
def get_grade_level(self):
if self.grade >= 90:
return "优秀"
elif self.grade >= 70:
return "良好"
else:
return "需努力"
# 创建3个学生
students = [
Student("小明", 12, 92),
Student("小红", 11, 85),
Student("小李", 13, 67)
]
# 批量介绍
for s in students:
s.introduce()
print(f" 评级:{s.get_grade_level()}")
print()
预期输出:
我叫小明,今年12岁,读12年级
评级:优秀
我叫小红,今年11岁,读11年级
评级:良好
我叫小李,今年13岁,读13年级
评级:需努力
一句话解释:class 把「数据+行为」打包,用列表可以批量管理多个对象。
项目 2(15 分钟):从 CSV 读取员工数据
背景:你有一份 employees.csv 文件,需要用 class 管理并统计
import csv
from pathlib import Path
class Employee:
def __init__(self, name, department, salary):
self.name = name
self.department = department
self.salary = salary
def get_bonus(self):
"""计算年终奖:工资的15%"""
return self.salary * 0.15
def display(self):
print(f"{self.name} | {self.department} | 月薪{self.salary} | 年终奖{self.get_bonus():.0f}")
# 读取CSV文件
def load_employees(filepath):
employees = []
with open(filepath, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
emp = Employee(
row['name'],
row['department'],
float(row['salary'])
)
employees.append(emp)
return employees
# 加载数据(请确保同级目录有 employees.csv)
csv_content = """name,department,salary
张三,技术部,15000
李四,市场部,12000
王五,技术部,18000
赵六,人事部,10000"""
# 为了演示,先创建临时CSV
Path('employees.csv').write_text(csv_content)
# 加载并显示
employees = load_employees('employees.csv')
print("=== 员工列表 ===")
for emp in employees:
emp.display()
# 统计技术部总支出
tech_total = sum(e.salary + e.get_bonus() * 12 for e in employees if e.department == "技术部")
print(f"\n技术部年度人力成本:{tech_total:.0f}")
预期输出:
=== 员工列表 ===
张三 | 技术部 | 月薪15000.0 | 年终奖2250
李四 | 市场部 | 月薪12000.0 | 年终奖1800
王五 | 技术部 | 月薪18000.0 | 年终奖2700
赵六 | 人事部 | 月薪10000.0 | 年终奖1500
技术部年度人力成本:430800
一句话解释:把 class 和文件读取结合起来,就能处理真实数据。
项目 3(15 分钟):命令行待办清单工具
目标:综合运用 class + 文件存储,做一个小型待办工具
import json
from pathlib import Path
from datetime import datetime
class TodoItem:
def __init__(self, title, completed=False):
self.title = title
self.completed = completed
self.created_at = datetime.now().strftime("%Y-%m-%d %H:%M")
def mark_done(self):
self.completed = True
def __str__(self):
status = "✅" if self.completed else "⬜"
return f"{status} {self.title}"
class TodoList:
def __init__(self, filename="todos.json"):
self.filename = filename
self.items = []
self.load()
def load(self):
"""从文件加载待办"""
if Path(self.filename).exists():
data = json.loads(Path(self.filename).read_text())
self.items = [TodoItem(t['title'], t['completed']) for t in data]
def save(self):
"""保存到文件"""
data = [{'title': item.title, 'completed': item.completed} for item in self.items]
Path(self.filename).write_text(json.dumps(data, ensure_ascii=False, indent=2))
def add(self, title):
self.items.append(TodoItem(title))
self.save()
print(f"已添加:{title}")
def done(self, index):
if 0 <= index < len(self.items):
self.items[index].mark_done()
self.save()
print(f"已完成:{self.items[index].title}")
else:
print("编号不存在")
def list_all(self):
print("\n===== 我的待办 =====")
if not self.items:
print("空空如也,休息一下吧~")
for i, item in enumerate(self.items):
print(f"{i+1}. {item}")
# 演示用法
todos = TodoList("my_todos.json")
todos.add("完成Chapter6笔记")
todos.add("复习class相关概念")
todos.list_all()
todos.done(0) # 标记第一个完成
todos.list_all()
预期输出:
已添加:完成Chapter6笔记
已添加:复习class相关概念
===== 我的待办 =====
1. ⬜ 完成Chapter6笔记
2. ⬜ 复习class相关概念
已完成:完成Chapter6笔记
===== 我的待办 =====
1. ✅ 完成Chapter6笔记
2. ⬜ 复习class相关概念
一句话解释:class 负责管理数据逻辑,文件操作负责持久化存储,分工明确。
💪 进阶 20 分钟:常见坑 + 小贴士
坑 1:__init__ 不是构造函数
# ❌ 错误理解
class Dog:
def __init__(self):
return "Hello" # __init__ 不能有返回值
# ✅ 正确理解
class Dog:
def __init__(self, name):
self.name = name # 初始化对象的属性
解释:__init__ 是「初始化方法」,不是构造函数,不能返回值。
坑 2:可变默认参数
# ❌ 危险写法
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] ← 期望是 ['b']!
# ✅ 正确写法
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
解释:默认参数在函数定义时创建一次,[] 是同一个列表。
坑 3:类属性 vs 实例属性
# ❌ 混淆
class Counter:
count = 0 # 类属性,所有实例共享
def __init__(self):
self.count += 1 # 这里创建了实例属性,没改类属性
c1 = Counter()
c2 = Counter()
print(c1.count) # 1(自己的)
print(c2.count) # 1(自己的)
print(Counter.count) # 0(还是类的)
# ✅ 正确理解
class Counter:
count = 0 # 类属性
def __init__(self):
Counter.count += 1 # 显式修改类属性
坑 4:忘记 self
# ❌ 报错
class Person:
def __init__(self, name):
name = name # 没有 self,只是局部变量
# ✅ 正确
class Person:
def __init__(self, name):
self.name = name # 绑定到实例
坑 5:命名空间混淆
# ❌ 容易出错
import math
print(math.pi)
from math import pi
print(pi) # 没问题,但混用时容易乱
# ✅ 清晰写法(选一种风格)
import math
print(math.pi)
调试技巧:print 大法
class Calculator:
def add(self, a, b):
print(f"DEBUG: a={a}, b={b}") # 加一行调试输出
return a + b
calc = Calculator()
result = calc.add(3, 5)
print(f"结果:{result}")
✏️ 练习题
练习 1(2 分钟):创建你的第一个类
- 输入:无
- 预期输出:
苹果的单价是:3.5元/斤
买了5斤苹果,总价:17.5元
- 提示:定义
Fruit类,有name、price、weight属性,get_total()方法
练习 2(3 分钟):加个判断
- 输入:沿用练习 1
- 预期输出:如果重量小于 1 斤,显示「太少了,不卖!」
- 提示:在 get_total() 里加 if self.weight < 1:
练习 3(5 分钟):用项目 2 读取新数据
- 输入:创建一个新的 CSV,内容换成「图书信息」(书名、作者、价格)
- 预期输出:打印所有书并计算最贵的一本
- 提示:复用 Employee 类的写法,改成 Book 类
练习 4(10 分钟):串个项目 2 和 3
- 输入:用项目 3 的待办清单结构,改造成「图书收藏夹」(书名、作者、已读/未读)
- 预期输出:能添加图书、标记已读、列表展示
- 提示:把 TodoItem 改成 BookItem,把 completed 改成 is_read
练习 5(5 分钟):报错分析
- 输入:下面代码运行后报什么错?
class User:
def __init__(self, name):
self.name = name
def say_hi():
print(f"你好,我是{self.name}")
u = User("小明")
u.say_hi()
- 预期输出:解释为什么错
- 提示:数一数
say_hi有几个参数
作业:做一个「个人账本工具」
需求描述:记录日常收入和支出,随时查看余额和统计
功能点:
1. 用 Transaction 类记录每笔交易(金额、类型、备注、时间)
2. 用 AccountBook 类管理所有交易(添加、查询、统计)
3. 数据保存到 JSON 文件,程序重启后不丢失
加分项:
1. 支持按月份筛选交易
2. 显示每月收支报表
验收标准:
- 能添加收入和支出
- 能显示当前余额
- 关闭程序再打开,数据还在
- 代码有注释
📚 总结
本文学了 3 个核心点:
1. class 把数据和行为打包成对象
2. 命名空间 用模块和包组织代码,避免冲突
3. 文件操作 让数据持久化,程序重启不丢失
延伸资源:
- Python 官方文档:类
- Real Python:Object-Oriented Programming
- 《Python编程:从入门到实践》- 第9章「类」
你在写什么项目时用过 class?有没有遇到过命名冲突的坑?评论区聊聊,老粉优先回复!
📝 下章预告:下一章我们会学到「继承」,就像家族族谱一样,类也能有「父子关系」——子类继承父类的所有能力,还能添加自己的特点。这在写大型项目时超级有用!

评论(0)