
摘要
在 Python 学习的中后期,我们开始接触“模块”和“包”这两个概念。很多人第一次听到它们时觉得有点抽象——到底模块是干嘛的?包又是什么?为什么 __name__ 打印出来有时候是 '__main__',有时候是别的名字?__all__ 又在控制什么?
这篇文章我们不讲太多概念,而是通过一个贴近现实的小项目来讲清楚这些问题。我们会模拟一个“学生管理系统”的雏形,通过模块拆分和控制导入范围,来展示模块化编程的思路和优势。
描述
在一个稍微有点规模的 Python 项目里,不可能把所有的类和函数都塞进一个文件。比如说你要写一个学生信息管理系统,里面有学生类(Student)、老师类(Teacher)、还有各种工具函数(比如格式化输出、计算年龄等)。
如果都写在一个 .py 文件里,后期维护就会很痛苦。
所以,模块化的意义就在于:
把逻辑相关的部分放在一个模块里,不相关的分开写。
而 __all__ 属性的作用,就是在“别人使用 from module import * 导入你的模块”时,控制他们能导入哪些内容。
__name__ 属性则更像是一个“程序入口标识”,帮助你判断当前模块是不是在直接运行。
下面我们就以一个“简单的学生信息管理系统”为例,边讲边用。
题解答案
我们会用三个文件来展示整个逻辑:
school_module.py— 模拟“功能模块”,包含Person、Student类和一些工具函数。test_name.py— 演示__name__属性的作用。test_school.py— 模拟主程序文件,导入模块并运行主要逻辑。
同时我们会用 __all__ 控制导入范围,让你直观感受到它的作用。
题解代码分析
模块文件:school_module.py
# school_module.py
# 定义模块的公开接口
__all__ = ['Student', 'func1']
# 人类
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 学生类,继承自 Person
class Student(Person):
def __init__(self, name, age, stu_id):
super().__init__(name, age)
self.stu_id = stu_id
def introduce(self):
print(f"大家好,我是{self.name},今年{self.age}岁,学号是{self.stu_id}。")
# 教师类(不在 __all__ 中,所以不会被 * 导入)
class Teacher(Person):
def __init__(self, name, age, subject):
super().__init__(name, age)
self.subject = subject
def introduce(self):
print(f"我是教师{self.name},教授{self.subject}课程。")
# 公共函数1
def func1():
print("func1() 被调用,用于输出系统欢迎信息。")
# 公共函数2
def func2():
print("func2() 被调用,用于输出调试信息。")
# 如果当前模块是主程序,则执行以下内容
if __name__ == '__main__':
print("当前运行的是 school_module.py 模块自身。")
代码说明:
-
__all__ = ['Student', 'func1']
表示当使用from school_module import *时,只能导入Student类和func1()函数。
Person和Teacher就不会被导入。 -
if __name__ == '__main__':
用来判断当前文件是否被直接运行。
如果是直接运行,则执行内部代码;如果是被导入,就不会执行那部分。
测试 __name__ 的文件:test_name.py
# test_name.py
print(__name__)
如果直接运行 test_name.py,输出为:
__main__
但如果我们在别的文件中导入它,比如:
import test_name
输出则变成:
test_name
这说明 __name__ 可以帮助我们区分“模块被直接运行”和“被别人导入”的情况,非常适合做调试或控制程序入口。
主程序文件:test_school.py
# test_school.py
from school_module import *
# func1 可用,因为在 __all__ 中
func1()
# Student 可用
student = Student("李明", 20, "20250101")
student.introduce()
# 下面两行会报错,因为不在 __all__ 中
try:
teacher = Teacher("张老师", 35, "数学")
teacher.introduce()
except NameError:
print("Teacher 未被导入,因为 __all__ 限制了导入范围。")
try:
func2()
except NameError:
print("func2() 未被导入,同样被 __all__ 限制。")
示例测试及结果
运行 test_school.py,输出结果如下:
func1() 被调用,用于输出系统欢迎信息。
大家好,我是李明,今年20岁,学号是20250101。
Teacher 未被导入,因为 __all__ 限制了导入范围。
func2() 未被导入,同样被 __all__ 限制。
结果非常直观地展示了:
__all__确实控制了导入范围;- 没有在
__all__里的内容不会被from ... import *导入; - 模块之间相互独立,代码结构清晰。
时间复杂度
这类模块化代码的主要逻辑是对象创建与函数调用,没有复杂的循环或递归。
- 对象初始化(
__init__)操作的时间复杂度是 O(1)。 - 模块导入时,Python 会将模块加载进内存,并缓存到
sys.modules,也属于 O(1) 的操作(一次性加载)。
所以整个程序的运行复杂度基本是常数级的。
空间复杂度
主要由以下几部分组成:
- 类定义和函数定义(静态占用);
- 对象实例化所占的内存(动态分配)。
整体空间复杂度依然是 O(1),除非你批量创建大量对象,否则不会显著增加内存消耗。
总结
通过这个例子我们能看到:
- 模块化 是 Python 工程化编程的基础。
它能让代码结构更清晰、复用更方便。 __name__用来区分“直接运行”还是“被导入”。
很多大型项目都会用if __name__ == '__main__':来定义程序入口。__all__则是模块“出口控制器”。
它决定别人通过from module import *能看到哪些内容。- 在实际项目中,这种控制方式能避免命名污染和不必要的函数泄露,让模块更加安全、可控。
总的来说,这就是 Python 模块和包的真正魅力——
清晰的结构 + 明确的边界 + 可控的访问。
不论是写小项目,还是搭大系统,这都是必须掌握的核心能力。


被折叠的 条评论
为什么被折叠?



