模块
前面聊了函数、类、文件操作这些东西,代码量一上来,一个 .py 文件里塞满几百行函数,看着就头大。这时候就该把代码拆一拆了,Python 里拆代码的基本单位就是模块(Module)。这一章咱们来看看模块是啥、怎么用、怎么管理第三方模块。
1. 什么是模块
说白了,一个 .py 文件就是一个模块。模块里面可以定义函数、类、变量,也可以包含可执行的代码。把功能相关的代码放到同一个文件里,取个有意义的名字,这就是一个模块。
比如 math.py 里放数学相关的函数,utils.py 里放通用工具函数,hello.py 里放打招呼的逻辑。
1.1 使用模块的好处
用模块组织代码,至少有这几个好处:
- 提高可维护性:文件变小了,定位问题、改逻辑都更方便。
- 提高复用性:写好的模块可以被其他程序反复引用,不用复制粘贴。
- 避免命名冲突:同一个项目里,
A模块里可以有个add()函数,B模块里也可以有个add()函数,互不影响。 - 利用内置和第三方模块:Python 自带了很多实用模块,社区还有海量第三方模块,能少写很多重复代码。
1.2 命名注意事项
自己写模块时,命名要遵循 Python 变量命名规范,别用中文、别用特殊字符。更重要的是,不要和系统模块重名。比如系统自带了 sys、os、json 这些模块,你就别把自己的文件也叫 sys.py、os.py、json.py,否则导入的时候会把自己导入进去,系统模块反而找不到了。
如果不确定某个名字有没有被占用,可以在交互式环境里试试:
import sys # 不报错,说明存在2. 导入模块
要使用模块里的功能,第一步就是把模块导入进来。导入方式主要有三种。
2.1 import 导入整个模块
这是最常用的方式,把模块整体引进来,使用时通过「模块名.函数名」调用。
import math
# 输出: 3.141592653589793
print(math.pi)
# 输出: 4.0
print(math.sqrt(16))这种方式的好处是命名空间清晰,一看就知道 sqrt 是从 math 模块来的。
2.2 from...import 导入指定内容
如果你只需要模块里的某个函数或变量,可以直接指定导入。
from math import pi, sqrt
# 输出: 3.141592653589793
print(pi)
# 输出: 4.0
print(sqrt(16))这样写代码更简洁,但缺点是当前命名空间里的 pi、sqrt 到底来自哪里,不如 import math 那么明显。如果项目里模块很多,容易出现命名冲突,要谨慎使用。
2.3 使用别名
有时候模块名太长,或者两个模块里有同名函数,可以给模块或函数起个别名。
import numpy as np
arr = np.array([1, 2, 3])
# 输出: [1 2 3]
print(arr)也可以给函数起别名:
from math import sqrt as square_root
# 输出: 5.0
print(square_root(25))起别名主要是为了代码可读性和方便性,建议别起一些只有自己看得懂的奇怪名字。
2.4 导入所有内容(不推荐)
Python 还支持 from module import *,会把模块里所有公开的名字都导入到当前命名空间。
from math import *
# 输出: 3.141592653589793
print(pi)虽然省事,但不推荐这么写。因为它会污染当前命名空间,还容易和现有变量冲突,代码可读性也差。
3. 模块文件的标准模板
一个规范的模块文件通常长这样:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' 模块的文档注释:说明这个模块是干啥的 '
__author__ = 'juzicoding'
import sys
def test():
args = sys.argv
if len(args) == 1:
print('Hello, world!')
elif len(args) == 2:
print(f'Hello, {args[1]}!')
else:
print('Too many arguments!')
if __name__ == '__main__':
test()逐行解释一下:
#!/usr/bin/env python3:在 Unix/Linux/Mac 上可以让文件直接./hello.py运行。# -*- coding: utf-8 -*-:声明文件使用 UTF-8 编码,避免中文乱码。' 模块的文档注释 ':模块的第一个字符串会被当作文档注释,可以用模块名.__doc__访问。__author__:作者信息,方便别人知道这代码是谁写的。
当然,如果你只是写个简单脚本,这些标准注释也可以省略。但养成好习惯,按标准来总没错。
4. if __name__ == '__main__' 的作用
这可能是每个学 Python 的人都会遇到的「天书」式代码,其实理解了就很简单。
Python 解释器在执行一个 .py 文件时,会给这个文件设置一个特殊变量 __name__:
- 如果这个文件是直接运行的,
__name__会被赋值为'__main__'。 - 如果这个文件是被其他文件导入的,
__name__会被赋值为模块名(比如'hello')。
所以 if __name__ == '__main__': 下面的代码,只有直接运行这个文件时才会执行,被导入时不会执行。
# hello.py
def test():
print('Hello, world!')
print(f'__name__ 的值是: {__name__}')
if __name__ == '__main__':
test()直接在命令行运行:
$ python hello.py
__name__ 的值是: __main__
Hello, world!在其他文件里导入:
import hello
# 输出: __name__ 的值是: hello这里被导入时只打印了
__name__,没有执行test(),说明if __name__ == '__main__':里的代码确实被跳过了。
这个写法常用于写一些测试代码、示例代码,或者让模块既能被复用,又能单独运行。
5. 作用域与命名规范
模块里定义的函数和变量,默认都是公开的(public),可以被外部引用。但有时候我们希望某些函数或变量只在模块内部使用,这时候可以通过命名前缀来约定。
5.1 public 与 private
- 公开:普通的函数名、变量名,比如
abc、PI、greeting()。 - 特殊变量:
__xxx__这种双下划线包起来的,比如__name__、__author__、__doc__,有 special 用途,自己写变量时不要用这种形式。 - 私有约定:
_xxx或__xxx这种以下划线开头的,表示「这是内部使用的,外部不应该直接引用」。
def _private_hello(name):
return f'Hello, {name}'
def greeting(name):
if len(name) > 3:
return _private_hello(name)
else:
return f'Hi, {name}'这里 _private_hello() 是内部辅助函数,外部调用 greeting() 就好,不用关心它内部怎么实现。
Python 没有严格限制不能访问私有变量或函数,
_xxx依然可以访问,但这是「约定俗成」,就像别人门上贴了「请勿打扰」,你硬要进去也能进,但很不礼貌。
6. 模块搜索路径
当我们执行 import xxx 时,Python 会按照一定的顺序去搜索 xxx.py 文件:
- 当前目录(执行脚本所在的目录)。
- Python 安装目录下的标准库。
- 第三方模块安装目录(通常是
site-packages)。
这些搜索路径都保存在 sys.path 列表里:
import sys
for path in sys.path:
print(path)输出大概长这样:
''
/usr/local/lib/python3.x/python3x.zip
/usr/local/lib/python3.x
/usr/local/lib/python3.x/site-packages第一个空字符串 '' 表示当前目录。
6.1 添加自定义搜索路径
如果想让 Python 能找到你自己写的模块,可以添加搜索路径。
方法一:临时添加 sys.path
import sys
sys.path.append('/Users/juzi/my_py_scripts')
import mymodule这种方式只在当前程序运行时有效,程序结束就失效。
方法二:设置 PYTHONPATH 环境变量
在系统环境变量里添加 PYTHONPATH,把自己的模块目录放进去。这样每次启动 Python 都会自动加载这些路径,推荐这种方式。
export PYTHONPATH=/Users/juzi/my_py_scripts:$PYTHONPATH如果搜索完所有路径都没找到模块,就会报 ModuleNotFoundError:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'mymodule'7. 包(Package)
模块是按文件组织的,那如果模块太多,文件也会很乱。Python 又引入了包(Package)的概念,包就是按目录组织的一组模块。
7.1 包的基本结构
一个包通常就是一个目录,目录里要有一个 __init__.py 文件(可以是空的),这样 Python 才会把这个目录当成普通包(regular package)。
mycompany
├── __init__.py
├── abc.py
└── xyz.py现在 abc.py 和 xyz.py 的模块名就变成了 mycompany.abc 和 mycompany.xyz。
从 Python 3.3 开始,即使没有
__init__.py,目录也可以被识别为命名空间包(namespace package),但传统项目仍然建议保留__init__.py,以避免兼容问题和意外行为。
import mycompany.abc
from mycompany import xyz7.2 __init__.py 的作用
__init__.py 本身也是一个模块,模块名就是包名。它会在导入包时自动执行,通常用来做一些初始化工作,比如批量导入子模块、设置 __all__ 等。
# mycompany/__init__.py
__all__ = ['abc', 'xyz']
__all__配合from mycompany import *使用,表示哪些子模块会被导出。
7.3 多级包
包下面还可以继续放子包,形成多级结构:
mycompany
├── __init__.py
├── abc.py
├── utils.py
└── web
├── __init__.py
├── utils.py
└── www.py这里 www.py 的全名就是 mycompany.web.www,两个 utils.py 分别是 mycompany.utils 和 mycompany.web.utils,互不冲突。
from mycompany.web import www
import mycompany.web.utils as web_utils包让模块的组织更清晰,但也别层级太深,一般两到三层就够用了,太深了写起来也麻烦。
8. 安装第三方模块
Python 的强大之处,很大程度上在于它有丰富的第三方模块。安装这些模块,主要靠 pip 这个包管理工具。
8.1 基本命令
安装一个模块:
pip install requests安装指定版本:
pip install requests==2.31.0升级模块:
pip install --upgrade requests卸载模块:
pip uninstall requests查看已安装的模块:
pip list如果你电脑上同时装了 Python 2 和 Python 3,Linux/Mac 上可能需要使用 pip3:
pip3 install requests8.2 PyPI 和常用源
第三方模块大多发布在 PyPI(Python Package Index)上,pip install 默认就是从这里下载。
如果下载慢,可以换国内的镜像源:
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple也可以把镜像源配置成默认的,省的每次都要写:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple8.3 常用第三方模块举例
这里简单提几个常用的,后面章节会细讲:
| 模块 | 用途 |
|---|---|
| requests | 发送 HTTP 请求 |
| Pillow | 图像处理 |
| numpy | 科学计算 |
| pandas | 数据分析 |
| flask / django | Web 开发框架 |
8.4 Anaconda
如果你做数据分析、科学计算,经常要装一大堆库,一个个用 pip 装很麻烦,还容易遇到依赖冲突。这时候可以考虑 Anaconda,它是一个集成了 Python 和很多常用第三方模块的平台,装好后 numpy、pandas 之类的库一般都能直接用。
9. 虚拟环境
不同项目可能依赖同一个模块的不同版本,比如项目 A 用 requests 2.25.0,项目 B 用 requests 2.31.0。如果把所有模块都装到系统 Python 环境里,很容易互相打架。这时候就要用到虚拟环境。
虚拟环境可以理解为一个独立的 Python 运行环境,每个项目一个环境,彼此隔离。
9.1 创建和激活虚拟环境
Python 3.3 以上版本内置了 venv 模块,不需要额外安装。
创建虚拟环境:
python -m venv myenv激活虚拟环境:
- Windows:
myenv\Scripts\activate- Mac/Linux:
source myenv/bin/activate激活后,命令行前面会出现 (myenv) 的标识,表示当前在虚拟环境里。这时候用 pip install 安装的包都只装在这个环境里,不会影响系统环境。
9.2 退出和删除虚拟环境
退出虚拟环境:
deactivate删除虚拟环境更简单,直接删掉目录就行:
rm -rf myenv养成好习惯,每个新项目都先创建一个虚拟环境,避免依赖冲突。
10. 总结
模块和包是 Python 组织代码的基础。掌握它们,代码才能写得规范、可维护、可复用。
本章要点
- 一个
.py文件就是一个模块,模块能提高代码的可维护性和复用性。 - 导入模块常用
import 模块名和from 模块名 import 函数/变量,模块名太长可以用as起别名。 if __name__ == '__main__':用来区分模块是「直接被运行」还是「被导入」。- Python 按照
sys.path中的路径搜索模块,可以通过PYTHONPATH添加自定义路径。 - 包是按目录组织的模块集合,目录下必须有
__init__.py,包支持多级嵌套。 - 第三方模块用
pip安装,国内建议换镜像源加速。 - 不同项目建议使用虚拟环境隔离依赖,避免版本冲突。