08-27-周三_17-09-29
This commit is contained in:
311
Python/Python面向函数/模块和包.md
Normal file
311
Python/Python面向函数/模块和包.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# 模块
|
||||
|
||||
**模块(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 处理可能的导入错误
|
||||
- 为可选功能提供优雅的降级方案
|
||||
- 提供清晰的错误信息和解决建议
|
Reference in New Issue
Block a user