# 模块 **模块(module)** 是一个包含 Python 代码的文件,通常以 `.py` 结尾。模块可以包含变量、函数、类,甚至其他模块。通过模块,我们可以将代码组织成不同的文件,更好地管理和复用代码。 模块可以分为以下几类: - **自定义模块**:用户自己编写的模块。 - **标准库模块**:Python 自带的模块,如 `math`、`os`、`sys` 等。 - **第三方模块**:由其他开发者编写的模块,通常可以通过 `pip` 安装。 # 自定义模块 自定义模块是 Python 中一个重要的代码组织方式,它允许开发者将相关的代码(包括函数、类和变量)封装到一个 `.py` 文件中。通过自定义模块,我们可以实现代码的重用和模块化开发,避免代码重复,提高代码的可维护性。自定义模块可以被其他 Python 程序通过 import 语句导入使用,每个模块都有自己独立的命名空间,这样可以避免不同模块之间的命名冲突。 **示例1:自定义模块** ```python # my_module.py print("This is my moudle....") def greet(name): return f"Hello, {name}!" def add(a, b): return a + b PI = 3.14159 # test.py import my_module # 使用 my_module 中的函数和变量 print(my_module.PI) print(my_module.greet("EaglesLab")) print(my_module.add(5, 3)) # 模块别名 import my_module as mm print(mm.greet("EaglesLab")) ``` **示例2:根据用户输入导入不同自定义模块** ```python # mysql.py def sqlparse(): print('from mysql sqlparse') # oracle.py def sqlparse(): print('from oracle sqlparse') # test.py db_type = input('>>: ') if db_type == 'mysql': import mysql as db elif db_type == 'oracle': import oracle as db db.sqlparse() ``` # 模块导入 ```python # 导入多个模块1 import sys, os, re # 导入多个模块2 import sys import os import re # 通过 from 导入模块 # from filename import function from my_module import greet, add # 覆盖导入模块中的同名函数 def add(): pass # 导入所有不是以下划线(_)开头的函数,可读性较差不推荐 from my_module import * # 设定 `from my_module import *` 导入哪些模块 ## my_module.py __all__ = ['greet','add'] ``` python 全局变量 `__name__`,用来控制 `.py` 文件在不同的应用场景下执行不同的逻辑 - 当文件被当做脚本执行时:`__name__ 等于'__main__'` - 当文件被当做模块导入时:`__name__等于模块名` ```python def fib(n): a, b = 0, 1 while b < n: print(b, end=',') a, b = b, a+b print() if __name__ == "__main__": print(__name__) num = input('num :') fib(int(num)) print(globals()) ``` # 模块的搜索路径 Python 在导入模块时会按照一定的顺序搜索模块所在的位置。主要的搜索路径包括: 1. **当前目录**:Python 首先在当前目录下查找要导入的模块 2. **PYTHONPATH 环境变量**:包含一系列目录名,可以通过设置此环境变量来添加额外的模块搜索路径 3. **标准库目录**:Python 安装时自带的标准库所在的目录 4. **site-packages 目录**:第三方模块安装的默认位置 可以通过以下方式查看和修改模块的搜索路径: ```python import sys # 查看当前的模块搜索路径 print(sys.path) # 添加自定义搜索路径 sys.path.append('/path/to/your/modules') # 在搜索路径开头添加目录(优先级更高) sys.path.insert(0, '/path/to/your/modules') ``` **设置PYTHONPATH环境变量**: ```bash # Linux/macOS export PYTHONPATH=/path/to/your/modules:$PYTHONPATH # Windows set PYTHONPATH=C:\path\to\your\modules;%PYTHONPATH% ``` 注意事项: 1. 不建议在代码中直接修改 `sys.path`,最好通过环境变量或安装包的方式管理模块路径 2. 搜索路径的优先级按照 `sys.path` 列表中的顺序,越靠前优先级越高 3. 在导入模块时,一旦在某个路径下找到了对应的模块,就会停止继续搜索 # 编译python文件 为了提高加载模块的速度,python 解释器会在`__pycache__`目录中下缓存每个模块编译后的版本,格式为:module.version.pyc。通常会包含 python 版本号。例如,在 CPython3.3 版本下,my_module.py 模块会被缓存成`__pycache__/my_module.cpython-33.pyc`。这种命名规范保证了编译后的结果多版本共存。 # 包 Python 包(Package)是一个包含 `__init__.py` 文件的目录,它用于组织和管理相关的Python模块。 包的主要作用是提供一种命名空间的层次结构,使得大型项目中的模块组织更加清晰。在Python3中,虽然__init__.py文件不是必需的,但为了保持兼容性和明确目录是一个包,建议始终创建这个文件。包可以包含子包和模块,通过包的层次结构可以避免命名冲突,提高代码的可维护性和重用性。 ## 包的使用 示例文件 ```python glance/ # Top-level package ├── __init__.py # Initialize the glance package ├── api # Subpackage for api │ ├── __init__.py │ ├── policy.py │ └── versions.py ├── cmd # Subpackage for cmd │ ├── __init__.py │ └── manage.py └── db # Subpackage for db ├── __init__.py └── models.py ``` 文件内容 ```python #文件内容 # policy.py def get(): print('from policy.py') # versions.py def create_resource(conf): print('from version.py: ',conf) # manage.py def main(): print('from manage.py') # models.py def register_models(engine): print('from models.py: ',engine) ``` ## 使用 import 导入包 ```python import glance.db.models glance.db.models.register_models('mysql') ``` 单独导入包名称时不会导入包中所有包含的所有子模块 ```python import glance # 在导入glance的时候会执行glance下的__init__.py中的代码 glance.cmd.manage.main() ``` 解决方法 ```python # glance/__init__.py from . import cmd # glance/cmd/__init__.py from . import manage ``` ## 使用 from 导入包 需要注意的是 from 后 import 导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:`from a import b.c`是错误语法 ```python from glance.db import models from glance.db.models import register_models models.register_models('mysql') register_models('mysql') ``` `from glance.api import *` 想从包api中导入所有,实际上该语句只会导入包api下 `__init__.py` 文件中定义的名字,我们可以在这个文件中定义`__all__` ```python x = 10 def func(): print('from api.__init.py') __all__= ['x','func','policy'] ``` ```python from glance.api import * func() print(x) policy.get() ``` # 绝对导入和相对导入 **绝对导入**:以执行文件的 sys.path 为起始点开始导入。 1. 优点:执行文件与被导入的模块中都可以使用 2. 缺点:所有导入都是以 sys.path 为起始点,导入麻烦 **相对导入**:参照当前所在文件的文件夹为起始开始查找,称之为相对导入 1. 符号:`.`代表当前所在文件的文件加,`..`代表上一级文件夹,`...`代表上一级的上一级文件夹 2. 优点:导入更加简单 3. 缺点:只能在导入包中的模块时才能使用 # 注意事项 1. **包的命名规范** - 包名应该简短、描述性强,全小写字母 - 避免使用Python保留字和标准库模块名 - 如果包名包含多个单词,建议使用下划线连接 2. **循环导入问题** - 避免模块之间的相互导入,这可能导致导入死锁 - 如果必须相互引用,可以考虑以下解决方案: - 将导入语句移到函数内部(延迟导入) - 重构代码结构,消除循环依赖 - 使用依赖注入模式 3. **包的初始化顺序** - `__init__.py` 文件在导入包时首先执行 - 避免在 `__init__.py` 中放置过多代码,保持简洁 - 初始化代码应该是幂等的(多次执行结果相同) 4. **版本兼容性** - 明确指定包的Python版本要求 - 使用条件导入处理不同Python版本的特性 - 在 `setup.py` 中声明依赖包的版本范围 5. **包的安装和分发** - 创建 `setup.py` 或 `pyproject.toml` 文件 - 包含必要的元数据(作者、版本、依赖等) - 使用虚拟环境进行开发和测试 - 确保包的文档和示例代码完整 6. **导入优化** - 避免使用 `from module import *`,这会污染命名空间 - 将频繁使用的模块导入放在文件顶部 - 使用相对导入(.)时要注意包的层级关系 7. **错误处理** - 在导入时使用 try-except 处理可能的导入错误 - 为可选功能提供优雅的降级方案 - 提供清晰的错误信息和解决建议