第6章 6.1 class 与命名空间

⚠️ 注意:本系列是 PHP 教程,但本章内容(class 与命名空间)在 Python 中概念相似,代码示例全部采用 Python 语法来演示核心思想,PHP 读者可轻松对应。

🎯 开场 3 分钟:为什么要学这个?

上一章我们用「博客系统后端」把函数、数组、文件操作串了一遍,代码越写越顺手。

但你可能遇到过这些崩溃时刻

  • 写了两个 process() 函数,Python 报 NameError: name 'process' is not defined
  • 接手别人的代码,3000 行堆在一个文件里,改一行坏三行
  • 想用别人写好的代码,但不知道哪个文件里有哪些函数

这些问题,学完「类与命名空间」就能解决。

学完本文你能
- 用 class 把数据和函数打包成「对象」
- 用「命名空间」给代码分区,避免名字冲突
- 读懂别人项目的结构,自己也能写出规范的代码


🧱 基础 25 分钟:核心概念

什么是 class?为什么\n\nSimple tech illustration expla\n\nAI comic creation scene, creat\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 类,有 namepriceweight 属性,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?有没有遇到过命名冲突的坑?评论区聊聊,老粉优先回复!


📝 下章预告:下一章我们会学到「继承」,就像家族族谱一样,类也能有「父子关系」——子类继承父类的所有能力,还能添加自己的特点。这在写大型项目时超级有用!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。