Files
Cloud-book/Python/Python面向函数/模块和包.md
2025-08-27 17:10:05 +08:00

8.7 KiB
Raw Blame History

模块

模块module 是一个包含 Python 代码的文件,通常以 .py 结尾。模块可以包含变量、函数、类,甚至其他模块。通过模块,我们可以将代码组织成不同的文件,更好地管理和复用代码。

模块可以分为以下几类:

  • 自定义模块:用户自己编写的模块。
  • 标准库模块Python 自带的模块,如 mathossys 等。
  • 第三方模块:由其他开发者编写的模块,通常可以通过 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 在导入模块时会按照一定的顺序搜索模块所在的位置。主要的搜索路径包括:

  1. 当前目录Python 首先在当前目录下查找要导入的模块
  2. PYTHONPATH 环境变量:包含一系列目录名,可以通过设置此环境变量来添加额外的模块搜索路径
  3. 标准库目录Python 安装时自带的标准库所在的目录
  4. 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%

注意事项:

  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文件不是必需的但为了保持兼容性和明确目录是一个包建议始终创建这个文件。包可以包含子包和模块通过包的层次结构可以避免命名冲突提高代码的可维护性和重用性。

包的使用

示例文件

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 为起始点开始导入。

  1. 优点:执行文件与被导入的模块中都可以使用
  2. 缺点:所有导入都是以 sys.path 为起始点,导入麻烦

相对导入:参照当前所在文件的文件夹为起始开始查找,称之为相对导入

  1. 符号:.代表当前所在文件的文件加,..代表上一级文件夹,...代表上一级的上一级文件夹
  2. 优点:导入更加简单
  3. 缺点:只能在导入包中的模块时才能使用

注意事项

  1. 包的命名规范

    • 包名应该简短、描述性强,全小写字母
    • 避免使用Python保留字和标准库模块名
    • 如果包名包含多个单词,建议使用下划线连接
  2. 循环导入问题

    • 避免模块之间的相互导入,这可能导致导入死锁
    • 如果必须相互引用,可以考虑以下解决方案:
      • 将导入语句移到函数内部(延迟导入)
      • 重构代码结构,消除循环依赖
      • 使用依赖注入模式
  3. 包的初始化顺序

    • __init__.py 文件在导入包时首先执行
    • 避免在 __init__.py 中放置过多代码,保持简洁
    • 初始化代码应该是幂等的(多次执行结果相同)
  4. 版本兼容性

    • 明确指定包的Python版本要求
    • 使用条件导入处理不同Python版本的特性
    • setup.py 中声明依赖包的版本范围
  5. 包的安装和分发

    • 创建 setup.pypyproject.toml 文件
    • 包含必要的元数据(作者、版本、依赖等)
    • 使用虚拟环境进行开发和测试
    • 确保包的文档和示例代码完整
  6. 导入优化

    • 避免使用 from module import *,这会污染命名空间
    • 将频繁使用的模块导入放在文件顶部
    • 使用相对导入(.)时要注意包的层级关系
  7. 错误处理

    • 在导入时使用 try-except 处理可能的导入错误
    • 为可选功能提供优雅的降级方案
    • 提供清晰的错误信息和解决建议