8.7 KiB
模块
模块(module) 是一个包含 Python 代码的文件,通常以 .py
结尾。模块可以包含变量、函数、类,甚至其他模块。通过模块,我们可以将代码组织成不同的文件,更好地管理和复用代码。
模块可以分为以下几类:
- 自定义模块:用户自己编写的模块。
- 标准库模块:Python 自带的模块,如
math
、os
、sys
等。 - 第三方模块:由其他开发者编写的模块,通常可以通过
pip
安装。
自定义模块
自定义模块是 Python 中一个重要的代码组织方式,它允许开发者将相关的代码(包括函数、类和变量)封装到一个 .py
文件中。通过自定义模块,我们可以实现代码的重用和模块化开发,避免代码重复,提高代码的可维护性。自定义模块可以被其他 Python 程序通过 import 语句导入使用,每个模块都有自己独立的命名空间,这样可以避免不同模块之间的命名冲突。
示例1:自定义模块
# 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:根据用户输入导入不同自定义模块
# 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()
模块导入
# 导入多个模块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__等于模块名
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 在导入模块时会按照一定的顺序搜索模块所在的位置。主要的搜索路径包括:
- 当前目录:Python 首先在当前目录下查找要导入的模块
- PYTHONPATH 环境变量:包含一系列目录名,可以通过设置此环境变量来添加额外的模块搜索路径
- 标准库目录:Python 安装时自带的标准库所在的目录
- site-packages 目录:第三方模块安装的默认位置
可以通过以下方式查看和修改模块的搜索路径:
import sys
# 查看当前的模块搜索路径
print(sys.path)
# 添加自定义搜索路径
sys.path.append('/path/to/your/modules')
# 在搜索路径开头添加目录(优先级更高)
sys.path.insert(0, '/path/to/your/modules')
设置PYTHONPATH环境变量:
# Linux/macOS
export PYTHONPATH=/path/to/your/modules:$PYTHONPATH
# Windows
set PYTHONPATH=C:\path\to\your\modules;%PYTHONPATH%
注意事项:
- 不建议在代码中直接修改
sys.path
,最好通过环境变量或安装包的方式管理模块路径 - 搜索路径的优先级按照
sys.path
列表中的顺序,越靠前优先级越高 - 在导入模块时,一旦在某个路径下找到了对应的模块,就会停止继续搜索
编译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文件不是必需的,但为了保持兼容性和明确目录是一个包,建议始终创建这个文件。包可以包含子包和模块,通过包的层次结构可以避免命名冲突,提高代码的可维护性和重用性。
包的使用
示例文件
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
文件内容
#文件内容
# 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 导入包
import glance.db.models
glance.db.models.register_models('mysql')
单独导入包名称时不会导入包中所有包含的所有子模块
import glance
# 在导入glance的时候会执行glance下的__init__.py中的代码
glance.cmd.manage.main()
解决方法
# glance/__init__.py
from . import cmd
# glance/cmd/__init__.py
from . import manage
使用 from 导入包
需要注意的是 from 后 import 导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c
是错误语法
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__
x = 10
def func():
print('from api.__init.py')
__all__= ['x','func','policy']
from glance.api import *
func()
print(x)
policy.get()
绝对导入和相对导入
绝对导入:以执行文件的 sys.path 为起始点开始导入。
- 优点:执行文件与被导入的模块中都可以使用
- 缺点:所有导入都是以 sys.path 为起始点,导入麻烦
相对导入:参照当前所在文件的文件夹为起始开始查找,称之为相对导入
- 符号:
.
代表当前所在文件的文件加,..
代表上一级文件夹,...
代表上一级的上一级文件夹 - 优点:导入更加简单
- 缺点:只能在导入包中的模块时才能使用
注意事项
-
包的命名规范
- 包名应该简短、描述性强,全小写字母
- 避免使用Python保留字和标准库模块名
- 如果包名包含多个单词,建议使用下划线连接
-
循环导入问题
- 避免模块之间的相互导入,这可能导致导入死锁
- 如果必须相互引用,可以考虑以下解决方案:
- 将导入语句移到函数内部(延迟导入)
- 重构代码结构,消除循环依赖
- 使用依赖注入模式
-
包的初始化顺序
__init__.py
文件在导入包时首先执行- 避免在
__init__.py
中放置过多代码,保持简洁 - 初始化代码应该是幂等的(多次执行结果相同)
-
版本兼容性
- 明确指定包的Python版本要求
- 使用条件导入处理不同Python版本的特性
- 在
setup.py
中声明依赖包的版本范围
-
包的安装和分发
- 创建
setup.py
或pyproject.toml
文件 - 包含必要的元数据(作者、版本、依赖等)
- 使用虚拟环境进行开发和测试
- 确保包的文档和示例代码完整
- 创建
-
导入优化
- 避免使用
from module import *
,这会污染命名空间 - 将频繁使用的模块导入放在文件顶部
- 使用相对导入(.)时要注意包的层级关系
- 避免使用
-
错误处理
- 在导入时使用 try-except 处理可能的导入错误
- 为可选功能提供优雅的降级方案
- 提供清晰的错误信息和解决建议