Python的模块和包
上一篇聊了《Go的模块和包》,可以看到,Java和Go中的模块都是比包(Package)更大的概念,但是Python中的模块却不是这样。
模块
在Python中,一个.py文件就是一个模块,模块名就是其文件名。
比如,我们创建一个pizza.py:
def make_pizza(size, *toppings):
"""制作比萨"""
print("Making a " + str(size) + "-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)这个pizza.py就是一个模块,模块名称为pizza。
在其他.py文件中,可以导入这个pizza模块来进行使用:
from pizza import make_pizza
make_pizza(16, "mushrooms", "green peppers")可以看到,Python的模块和JavaScript中的模块很像。如果改成JavaScript的写法:
import {make_pizza} from './pizza.js'两者的相同点就是:模块即文件,Python中是.py文件,JavaScript中是.js文件。
命名规范
Python的模块名建议使用简短的全小写名称,如果模块名有多个单词,可以在单词之间使用下划线来提高可读性。比如下面两个模块:
- my_module.py:模块名称为my_module,单词之间用下划线。
- os.py:模块名称为os,使用简短的全小写名称。
模块中的__name__
需要注意一个重要的细节:
- 当一个模块被作为主程序入口直接运行时(比如 python my_script.py),它的 __name__ 属性会被设置为 '__main__'。
- 当一个模块被其他模块导入时(import my_script),此时 __name__ 属性就是模块名 'my_script'。
包(Package)
在Python中,包是由模块组成的文件夹,其中包含一个__init__.py文件。
假设有一个处理音频的项目,它的文件结构可能是这样的:
audio/ # 这是一个包 (Package)
├── __init__.py # 标记audio为包的必需文件(可以为空)
├── effects/ # 这也是一个包 (子包)
│ ├── __init__.py
│ ├── echo.py # 这是一个模块 (Module)
│ ├── surround.py # 这是一个模块
│ └── reverse.py # 这是一个模块
├── filters/ # 另一个子包
│ ├── __init__.py
│ ├── equalizer.py # 模块
│ └── compressor.py # 模块
└── utils.py # 这是一个模块如上,这个audio项目一共有三个包:
- audio包:有两个子包(effects和filters),以及一个模块
- effects包:有三个模块
- filters包:有两个模块
每个包目录下都有一个__init__.py文件。这是Python的规定,只有包含__init__.py文件的目录才会被识别为包。当首次加载包时,Python会自动执行该文件来完成包的初始化。这个文件类似NPM包的 index.js 的作用。
命名规范
包名建议使用简短的全小写名称,和模块名不一样,不建议使用下划线。比如下面三个包:
- requests: 只有一个单词,全小写命名
- matplotlib: 两个单词,中间不使用下划线
- fastapi:两个单词,中间不使用下划线
总结
对于稍微复杂一点的项目来说,模块和包是不可或缺的工具,无论是弄清楚其他人的项目结构,还是自己编写易维护的项目,都需要遵循良好的约定。
对于模块和包的概念,Go和Java的设计有点类似,而Python则和JavaScript类似。这是因为Java/Go 诞生于大型工程,它们从设计之初就面向大规模、高并发、严苛依赖管理的系统。而Python和JavaScript都是脚本语言,最初追求的是快速、轻量、直觉。一个文件就是一个独立运行的脚本或模块,这种心智模型非常容易理解。
参考
- Naomi Ceder. The Quick Python Book, Third Edition. Manning, 2018.
- Eric Matthes. Python Crash Course. no starch press, 2016.