只有理解了模块和包才能写出符合 Python 社区规范维护的代码。包和模块基本一起使用,它们的关系,可以简单的理解为:文件和文件夹的关系。
一、Python 非模块的代码组织
在 Python 中不使用模块组织代码也是可以的。它适用于以下类型:
- 简单的函数、类、变量、命令行工具使用。
- 文件组织相对灵活与松散。
- 适合一次性任务、简短和实验的原型开发。
- 更加适合快速开发的小项目。
如果项目上了一定的规模,就需要有模块系统作为支撑了。统一的模块和编程范式能够帮助快速的帮助编写可维护的代码。
二、Python 模块
2.1) Python 模块的定义
Python 模块是一个 .py 文件,包含 Python 代码的内容:变量、函数、类、代码块等等。使用模块有利于代码的分组、封装和重用。
touch main.py # 创建一个 main.py 模块文件
2.2) Python 模块内容
- import 语句
- 声明变量
- 声明函数
- 声明 class
- 其他表达式和代码块
- 注释(docstring)和文档
- ...
"""
这是一个 Python 模块(包含注释)提供了主要的 Python 模块内容。
"""
# 一个简单的全局导入模块
import example
# 定义变量
variable_one = 123
# 定义函数
def func_num():
"""函数注释, 空函数"""
pass
# 定义类
class Adb:
def __init__():
pass
# 定义条件语句
if __name__ == "__main__":
pass
2.3) Python 使用模块
这是一个模块示例:
my_project/
main.py
utils/
__init__.py
helpers.py
another_module.py
绝对导入
绝对导入是指通过模块的完整路径来导入模块。它基于模块的完整路径,从顶层包开始。
from utils import helpers
utils 是一个包,它包含了 helpers。并且 main.py 是跟目录,包的绝对导入是从根目录开始往下找的。
相对导入
相对导入与绝对导入不同,相对导入是从当前文件作为开始目录:
- 单个点(.):表示当前目录。
- 两个点(..):表示上一级目录。
utils 中的 __init__.py 使用 helpers 的方式如下。
# utils/__init__.py
from . import helpers # 从当前目录导入 helpers 模块
在 helpers.py 中使用 another_module.py, 需要加上表示当前文件夹的 . 符号
from .another_module import some_function
2.4) 全局导入
from utils.helpers import *
使用 * 表示全部导入。
2.5) 导入指定内容
from utils.helpers import some_func
从 utils.helpers 函数中导入 some_func 内容。
2.6) 局部导入
局部导入是指在函数或代码块内部导入模块。通常用于避免循环导入或优化启动时间。
def my_function():
import utils.helpers # 在函数内部导入模块
utils.helpers.some_function()
2.7) 导入别名
使用 as 关键字表示导入别名
import utils.helpers as help
from utils.helpers import some_func as func
as 关键字将导入的内容重命名,原来的变量名字失效。
2.8) 动态导入
内置动态 __import__() 动态导入,基本语法
__import__(name, globals=None, locals=None, fromlist=(), level=0)
- name: 要导入的模块名,作为字符串传递。
- globals: 可选参数,传递给导入模块的全局命名空间,通常设置为 globals()。
- locals: 可选参数,传递给导入模块的局部命名空间,通常设置为 locals()。
- fromlist: 可选参数,指定从模块中导入的内容。如果是空元组(默认值),则返回顶级模块;如果指定内容,则返回包含这些内容的模块。
- level: 可选参数,指定导入的层级。0 表示绝对导入,正整数表示相对导入。
以下是一个简单的示例:
# 使用 __import__() 动态导入模块
math_module = __import__('math')
# 使用导入的模块
result = math_module.sqrt(16)
print(result)
# 导入子包
os_path = __import__('os.path')
__import__() 以函数的形式使用,使用起来并不直观。推荐以下的内容
导入库 importlib
importlib 也是标准库。以下是 importlib 的基本使用示例:
import importlib
math = importlib.import_module('math')
sin = getattr(math, 'sin')
radians = getattr(math, 'radians')
angle_in_degress = 30
angle_in_radians = radians(angle_in_degress)
print(sin(angle_in_radians))
- 使用 import_module 函数动态的导入 math 函数。
- 使用 getattr 函数获取
importlib 是 Python 3 提供的标准模块,相对与 __import__() 功能更加灵活强大。所以在动态的导入一般情况推荐使用 importlib 导入代码。
2.9) 模块的特殊属性
下面是 Python 模块特殊属性的表格总结:
属性 | 描述 | 主要用途 |
__name__ | 模块的名称 | 判断模块是被直接运行还是被导入 |
__file__ | 模块文件的路径 | 获取模块的文件路径 |
__doc__ | 模块、函数、类的文档字符串 | 访问和显示文档字符串 |
__package__ | 模块的包名 | 处理包内的相对导入 |
__spec__ | 模块的 ModuleSpec 对象 | 提供模块的加载器和定位器信息 |
__cached__ | 模块的缓存文件路径 | 检查和管理 .pyc 缓存文件 |
__annotations__ | 函数、方法或类的注解 | 提供函数参数和返回值的注解信息 |
控制 Python 文件行为
一个文件两种用途:
- 作为模块用
- 最为可执行文件用
能两用的关键是内置 __name__ 可以做条件区分:
# module.py
def hello():
pass
if __name__ == "__main__":
hello()
- 当此 Python 文件被当做模块导入了 hello 函数,下面的 if 语句不会执行。
- 如果这个函数被单独执行了,那么会执行 if 语句。
三、Python 模块路径搜索
- 当前工作目录:Python 会首先在当前目录中查找模块。
- 标准库目录:包含 Python 自带的标准库模块。
- PythonPATH 环境变量:用户定义的额外目录,用于扩展模块搜索路径。
- 安装的第三方库目录:包含通过包管理工具安装的第三方库,如 site-packages 目录。
四、Python 包
my_package/
__init__.py
module1.py
module2.py
sub_package/
__init__.py
sub_module1.py
sub_module2.py
4.1) 定义包
包含 __init__.py 文件的目录,用于组织模块。
4.2) 创建包
需要创建一个 __init__.py 文件, 包的初始化代码和公共接口定义。
4.3) 包的导入
与模块相似,在包内部使用点表示模块路径
五、Python 包管理和分发
setuptools 是一个用于创建、管理和分发 Python 包的工具,它扩展了 Python 的标准库 distutils。setuptools 提供了很多功能,如依赖管理、自动生成安装包等,简化了 Python 包的开发和分发过程。
5.1) 安装 setuptools
setuptools 可以通过 pip 安装:
pip install setuptools
如果你已经安装了 pip,可以通过以下命令更新 setuptools 到最新版本:
pip install --upgrade setuptools
5.2) 使用 setuptools 创建和管理包
1. 创建包的目录结构
假设你要创建一个名为 my_package 的包,目录结构如下:
my_package/
my_package/
__init__.py
module1.py
module2.py
tests/
__init__.py
test_module1.py
setup.py
README.md
- my_package/:包的主目录,包含 __init__.py 和模块文件。
- tests/:包含测试文件的目录。
- setup.py:包的配置信息。
- README.md:包的描述文件。
2. 编写 setup.py
setup.py 是 setuptools 使用的配置文件,用于定义包的元数据和其他配置信息。以下是一个基本的 setup.py 示例:
from setuptools import setup, find_packages
setup(
name='my_package',
version='0.1',
packages=find_packages(),
install_requires=[
# 列出你的包依赖的其他库
],
description='A brief description of my_package',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
author='Your Name',
author_email='your.email@example.com',
url='https://github.com/yourusername/my_package',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
],
)
配置项 | 描述 |
name | 包的名称 |
version | 包的版本号 |
packages | 自动查找包和子包 |
install_requires | 列出包的依赖 |
description | 简短描述包的功能 |
long_description | 从 README.md 文件读取详细描述 |
long_description_content_type | 描述文件的格式 |
author | 作者信息 |
author_email | 作者的电子邮箱 |
url | 包的主页或 GitHub 地址 |
classifiers | 包的分类信息,用于 PyPI 上的搜索和筛选 |
3. 创建分发包
使用 setuptools 创建源代码分发包和二进制分发包:
python setup.py sdist bdist_wheel
- sdist:创建源代码包(.tar.gz)。
- bdist_wheel:创建二进制包(.whl)。
这些文件会被生成在 dist/ 目录中。
4. 安装包
# 可以使用 `pip` 安装生成的包:
pip install dist/my_package-0.1-py3-none-any.whl
# 或者从源代码包安装:
pip install dist/my_package-0.1.tar.gz
5. 发布包到 PyPI
如果你想将包发布到 Python 包索引(PyPI),可以使用 twine 工具:
- 安装 twine:pip install twine
- 使用 twine 上传包:twine upload dist/*(你需要一个 PyPI 账户,并在上传时输入用户名和密码。)
六、Python 命名和组织
以下是对命名规范和目录结构的内容整理成了表格形式:
命名规范
规则 | 描述 | 示例 |
小写字母 | 模块和包的名称通常使用小写字母 | my_module, data_processor |
短名称 | 名称应简洁明了,能反映模块或包的功能 | utils, parser |
下划线 | 多单词组合的模块或包名称使用下划线 _ 连接 | data_processor, file_manager |
避免冲突 | 避免与 Python 标准库中的模块名称冲突 | my_custom_json 而非 json |
目录结构
规则 | 描述 | 示例 |
按功能分组 | 将相关功能的模块和包放在一起,便于组织和维护 | data_processing/, auth/ |
层次结构 | 使用包和子包表示项目的不同层次或模块间的依赖关系 | project_name/, project_name/utils/ |
代码分离 | 将应用程序代码、测试代码、配置文件等分开存放 | src/, tests/, configs/ |
七、小结
本文主要讲解内容:
- Python 非模块下的代码组织以及局限性
- Python 模块定义
- Python 模块的属性和方法
- Python 模块的使用(导入、动态导入)
- Python 模块的路径搜索
- Python 包的基础知识
- Python 的管理与分发
- Python 包和模块的命名与代码组织。