Python包模块与模块导入查找顺序详解

 更新时间:2025年09月02日 09:53:38   作者:trayvontang  
Python模块是.py文件,包含__init__.py,导入查找顺序为sys模块缓存、当前目录、PYTHONPATH、Lib、site-packages,命名空间分local、global、built-in,文件类型包括.pyc、.pyd、.dll等,用于存储编译结果

简介

无论是为了看懂别人的代码,还是为了更好的组织我们的个人的工程代码,了解一下Python的模块和包都非常有必要。

另外,知道搜索模块的顺序,也能帮助我们更好的理解一些常见的错误,方便我们快速定位问题。

模块导入查找顺序

首先,我们先看一下Python的模块查找顺序:

  1. sys.modules查找(导入模块缓存)
  2. 执行脚本所在的目录:__name__==‘__main__’
  3. 环境变量PYTHONPATH指定的目录
  4. 标准库目录(Python安装跟目录下的Lib目录)
  5. 配置文件(Python安装跟目录下.pth文件中指定的目录)
  6. 三方库(Python安装跟目录下site-packages中的目录)

搜索路径是由sys.path决定,我们可以打印看一下:

import sys

print(sys.path)

输出:

['E:\\app\\python\\fin\\md', 'E:\\app\\python\\fin', 'E:\\app\\python\\fin\\.venv\\Scripts\\python313.zip', 'D:\\Env\\python\\py3130i\\DLLs', 'D:\\Env\\python\\py3130i\\Lib', 'D:\\Env\\python\\py3130i', 'E:\\app\\python\\fin\\.venv', 'E:\\app\\python\\fin\\.venv\\Lib\\site-packages']

我们可以手动添加搜索目录和顺序:

import sys

# 添加在末尾,最后查找
sys.path.append('dir')
# 添加在开头,最先查找
sys.path.insert(0, 'dir2')

# 支持zip
sys.path.append('module.zip')
# 支持zip中指定目录
sys.path.append('module.zip/lib/python')

模块查找

入口文件所在目录

环境变量PYTHONPATH

  • Linux系统:
echo 'export PYTHONPATH="/home/tim/pylib/"' >> ~/.bashrc
source ~/.bashrc

  • Windows:

Lib目录(标准库)

.pth配置文件

注意._pth和.pth的区别

._pth:安装包中没有这个文件要自己创建,内嵌包中有

内容如下:

python311.zip
.
D:\\Env\\python\\py3114\\Lib\\site-packages\\
# Uncomment to run site.main() automatically
#import site

._pth会覆盖sys.path

而.pth是添加sys.path

.pth的处理逻辑在标准库site中:

新版本的.pth文件有很多限制,可以参考:site

注意:

这个文件名字必须要对,不知道,可以直接复制安装目录上的PythonXXX.dll这个文件的名字。

另外,一般不需要修改,因为这个文件会覆盖sys.path,让其他的都失效。

三方库

标准库Lib下的site-packages目录

模块

Python中的模块就是指py文件,一个文件代表一个模块。

很多时候,一个工程不止一个模块,我们可以用包来组织不同模块。

包中包含一个__init__.py文件,用来控制包的导入行为。

在2.x中__init__.py文件是必修有,3.x可选。

import

import 模块名

Python解释器第1次导入模块就加载到内存,多次import不会重新执行模块内的语句

我们看一个示例:

我们有一个base_module.py文件

print('这是一个自定义基本模块')

num = 1000


def fun_a():
    print('fun_a', num)


def fun_b():
    print('fun_b')

if __name__=='__main__':
    print('执行了该文件')

我们在module_import_test.py文件中导入base_module模块(文件)

import sys

print(sys.modules)

import base_module

# 多了base_module模块信息
print(sys.modules)
base_module.fun_a()

import base_module
print(sys.modules)

base_module.fun_a()

注意:文件名字不要用中划线,否则import模块会出错。

我们可以看到没有打印:执行了该文件,这是因为我们导入的是模块,没有直接执行这个文件。

现在是不是就更清楚为啥要加上:if name==‘main’:了

if __name__=='__main__':
    print('执行了该文件')

from mmm import fffvvv

从包或者模块中导入函数、变量等。

和直接import模块不同,from方式相当于将模块指定的元素导入到当前命名空间。

因此,我们可以直接调用方法,不用通过模块名.xxx的方式调用了。

from base_module import fun_a,fun_b

fun_a()
fun_b()

from mmm import fff as new_name

还可以用as重命名:

from base_module import fun_a as xxx

xxx()

form mmm import *

*表示将模块中除了下划线(_)开头的元素全部元素导入

from base_module import *

fun_a()
fun_b()
print(num)

__all__控制import *的行为

我们在模块中添加__all__,注意是一个字符串列表,下面是fun_a

print('这是一个自定义基本模块')

num = 1000


def fun_a():
    print('fun_a', num)


def fun_b():
    print('fun_b')


__all__ = ['fun_a']

if __name__=='__main__':
    print('执行了该文件')

我们再调用fun_b(),就会出现NameError:

相对导入与绝对导入

  • pkg_b.py
num_b = "num_b"

def fun_b():
    print("pkg_b fun_b")
  • pkg_c.py
from .pkg_b import fun_b
from . import pkg_b
num_c = "num_c"

def fun_c():
    print("pkg_c fun_c")


fun_b()
print(f"pkg_c中导入pkg_b:{pkg_b.num_b}")
  • base_import.py
import pkg_test.sub_pkg.pkg_b

from pkg_test.sub_pkg import pkg_b
from pkg_test.sub_pkg import pkg_c

pkg_test.sub_pkg.pkg_b.fun_b()

pkg_b.fun_b()

pkg_c.fun_c()
pkg_c.pkg_b.fun_b()

相对导入的模块不能作为主程序运行,因为.会被替换为__main__,然后就会出现:

ImportError: attempted relative import with no known parent package

小结

导入包和导入模块基本一致,如何判断是包还是模块呢?

包通常是有结构,所以,看到import xxx.yyy这种包含.的import,就是导入的包。

import 包名.模块名 as 别名
from . import 模块名
from 包名 import 模块名 as 别名
from 包名.模块名 import 成员名 as 别名

更多内容可以参考:

官方import说明

包说明

命名空间

Python的命名空间有点类似于变量表。

  1. local namespace,就是函数的命名空间,相当于局部变量表,包含函数的变量
  2. global namespace,就是模块的命名空间,包含functions、classes、modules、变量、常量等
  3. build-in namespace,包含build-in function、exceptions,所有模块均可访问

Python对于变量的查找顺序为:

  1. local namespace,当前函数或类方法,若找到,则停止搜索
  2. global namespace,当前模块,若找到,则停止搜索
  3. build-in namespace,最后查找build-in命名空间
  4. 若变量不是build-in的内置函数或变量,Python将报错NameError

闭包比较特殊:在local namespace找不到该变量,会查找目标是父函数的local namespace

看一个简单的示例:

print('这是一个自定义基本模块')

num = 1000


def fun_a():
    print('fun_a', num)

def fun_b():
    print('fun_b')


if __name__=='__main__':
    fun_a()
    print('执行了该文件')
from base_module import fun_a,num

print(num)
num = 5000
print(num)
fun_a()

我们可以看到,改变了num值,调用模块中的方法的时候,还是方法的模块直接的命名空间的num。

各类文件与目录

  1. .pyo:优化编译后的文件
  2. .pyc:Python编译之后的文件
  3. .pyd:包含Python代码的库文件(Windows)
  4. .pyi:静态类型信息文件
  5. .so:Linux的动态链接库
  6. .dll:Windows的动态链接库
  7. __pycache__:用于存储编译Python源文件的字节码文件目录(sys.dont_write_bytecode开关控制)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Python数据分析与处理(二)——处理中国地区信息

    Python数据分析与处理(二)——处理中国地区信息

    这篇文章主要介绍了Python数据分析与处理-处理中国地区信息,上文介绍了北京高考分数线统计分析,这篇文章依然围绕Python数据分析与处理的相关资料来介绍处理中国地区信息,需要的朋友可以参考一下
    2021-12-12
  • Python3读写ini配置文件的示例

    Python3读写ini配置文件的示例

    这篇文章主要介绍了Python3读写ini配置文件的示例,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下
    2020-11-11
  • pandas重置索引标签的实现示例

    pandas重置索引标签的实现示例

    在使用Pandas进行数据处理时,有时候我们可能会需要对数据进行重置索引的操作,本文主要介绍了pandas重置索引标签的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-04-04
  • Python基础教程之异常详解

    Python基础教程之异常详解

    调试Python程序时,经常会报出一些异常,下面这篇文章就来给大家介绍了关于Python基础教程之异常的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2019-01-01
  • Python reduce函数作用及实例解析

    Python reduce函数作用及实例解析

    这篇文章主要介绍了Python reduce函数作用及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • Python破解excel进入密码的过程详解

    Python破解excel进入密码的过程详解

    这篇文章主要为大家介绍了如何利用Python破解excel进入密码,文中的示例代码讲解详细,对我们学习Python有一定帮助,感兴趣的可以动手试一试
    2022-03-03
  • Python实现双向RNN与堆叠的双向RNN的示例代码

    Python实现双向RNN与堆叠的双向RNN的示例代码

    这篇文章主要为大家详细介绍了如何利用Python语言实现双向RNN与堆叠的双向RNN,文中详细讲解了双向RNN与堆叠的双向RNN的原理及实现,需要的可以参考一下
    2022-07-07
  • Pytorch微调BERT实现命名实体识别

    Pytorch微调BERT实现命名实体识别

    命名实体识别(NER)是自然语言处理(NLP)中的一项关键任务,它涉及识别和分类文本中的关键实体,BERT是一种强大的语言表示模型,在各种 NLP 任务中显著提高了性能,包括 NER,在本文中,我们将展示如何使用 PyTorch 对预训练的 BERT 模型进行微调,以用于 NER 任务
    2025-03-03
  • python中Requests发送json格式的post请求方法

    python中Requests发送json格式的post请求方法

    这篇文章主要介绍了python中Requests发送json格式的post请求方法,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的朋友可以参考一下
    2022-09-09
  • Python实现求取表格文件某个区域内单元格的最大值

    Python实现求取表格文件某个区域内单元格的最大值

    这篇文章主要介绍基于Python语言,基于Excel表格文件内某一列的数据,计算这一列数据在每一个指定数量的行的范围内(例如每一个4行的范围内)的区间最大值的方法,需要的朋友可以参考下
    2023-08-08

最新评论