探寻Python导包路径机制

2021-12-06 20:35:44 浏览数 (1)

引言

为什么我们 import os, improt sys, improt math等模块,就可以成功导入其模块,而随便 import aaa,就不行呢。那是因为 Python 的导包路径原因,让我们来康康 Python 的导包路径,是怎样的机制。

查看导包路径

可以通过内置 sys 模块来查看导包路径。

代码语言:javascript复制
In [1]: import sys

In [2]: sys.path
Out[2]:
['D:\Hui\DevelopEnv\Python\Python379\Scripts\ipython.exe',
 'd:\hui\developenv\python\python379\python37.zip',
 'd:\hui\developenv\python\python379\DLLs',
 'd:\hui\developenv\python\python379\lib',
 'd:\hui\developenv\python\python379',
 '',
 'd:\hui\developenv\python\python379\lib\site-packages',
 'd:\hui\developenv\python\python379\lib\site-packages\IPython\extensions',
 'C:\Users\Administrator\.ipython']

In [3]:

sys.path 返回的是一个路径列表,其代表 Python导包时搜素的路径

  • Python解释器sys.path 里依次查找要导入的模块文件或包
  • '' 表示当前路径
  • sys.path 列表中的路径的先后顺序代表了 Python解释器 在搜索模块时的先后顺序

内置模块、包存放路径

os, sys, json 等一些内置模块、包都存放在你下载 Python解释器 时,其保存路径的 Lib 目录

存放路径以我个人的举例:

代码语言:javascript复制
D:HuiDevelopEnvPythonPython379Lib

下载的第三方库存放路径

requestsipython 这些自己下载的第三方库等都存放在 Lib 下的 site-packages 目录下

存放路径以我个人举例

代码语言:javascript复制
D:HuiDevelopEnvPythonPython379Libsite-packages

然而导包路径 sys.path 就包含这两个路径

因此我们使用 import osimport sysimport jsonimport requests 等都可以找到相应的模块和包

如果导入模块和包时在 sys.path 中没有搜索到相对应的模块,则会报如下错误

代码语言:javascript复制
ModuleNotFoundError: No module named 'xxx'
代码语言:javascript复制
import aaa
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-4-37ad1770aa41> in <module>
----> 1 import aaa

ModuleNotFoundError: No module named 'aaa'

其原理跟我们的电脑的 环境变量-Path 有点像。

我们可以在 cmd窗口输入 python 可以打开 python 交互解释器pip install xxx 可以下载第三方库。都是因为 ·系统环境变量-Path,有具体可执行文件的路径

追加新的导包路径

我们可以在程序运行时动态追加新的导包路径,代码如下

代码语言:javascript复制
sys.path.append('D:HuiCodePythondemo')		# 追加到末尾

sys.path.insert(0, 'D:HuiCodePythondemo')   # 追加到开头位置,可以确保先搜索这个路径

ipython 测验

现在 D:HuiCodePythondemo 目录下有一个 aaa.py 模块。

aaa.py 模块内容如下

代码语言:javascript复制
# aaa.py

def test():
	print('追加导包路径成功')

导包路径没追加 D:HuiCodePythondemo 时,import aaa 会报错

追加之后,在试试

追加导包路径之后就可以成功导入并使用了。

Django项目追加导包路径

来康康导包路径的具体应用场景。

Django 中我们通常把子应用模块统一放在 apps 包下,但在注册子应用的时候,该如何设置路径呢?

我们在配置文件 settings.py or develop.py 中添加打印导包路径的代码

代码语言:javascript复制
import sys
from pprint import pprint

pprint(sys.path)

其中 pprintpretty print 美化输出的意思,这样输出的列表不会在一行上。

然后运行 Django 程序查看导包路径结果

代码语言:javascript复制
['C:\Users\Administrator\Desktop\meiduo_project\meiduo_mall',
 'C:\Users\Administrator\Desktop\meiduo_project',
 'D:\Hui\DevelopTools\PyCharm '
 '2020.2.3\plugins\python\helpers\pycharm_display',
 'd:\hui\developenv\python\python379\python37.zip',
 'd:\hui\developenv\python\python379\DLLs',
 'd:\hui\developenv\python\python379\lib',
 'd:\hui\developenv\python\python379',
 'D:\Hui\VirtualEnv\meiduo_mall',
 'D:\Hui\VirtualEnv\meiduo_mall\lib\site-packages',
 'D:\Hui\DevelopTools\PyCharm '
 '2020.2.3\plugins\python\helpers\pycharm_matplotlib_backend']

已知导包路径

  • meiduo_project/meiduo_mall

已知 users应用所在目录

  • meiduo_project/meiduo_mall/meiduo_mall/apps/users

因此导入users 应用的路径可以写为:meiduo_mall/apps/users

知道导包路径我们就好在配置文件 settings.py or develop.py 中注册子应用

代码语言:javascript复制
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    'meiduo_mall.apps.users',	# 注册用户模块
]

是否可以将注册 users 应用做的更加简便?按照如下形式,直接以应用名 users 注册

代码语言:javascript复制
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    'users',	# 注册用户模块
]

分析:

  • 已知导包路径
    • meiduo_project/meiduo_mall
  • 已知 users 应用所在目录
    • meiduo_project/meiduo_mall/meiduo_mall/apps/users
  • 若要直接以应用名 users 注册
    • 需要一个导包路径:meiduo_project/meiduo_mall/meiduo_mall/apps

解决办法

  • 追加导包路径:meiduo_project/meiduo_mall/meiduo_mall/apps

在配置文件 settings.py or develop.py 中追加导包路径

代码语言:javascript复制
sys.path.insert(0, r'meiduo_project/meiduo_mall/meiduo_mall/apps/users')

在项目中一般不会写死路径,因此利用 BASE_DIR 来动态拼接路径

打印 BASE_DIR 内容如下

代码语言:javascript复制
'C:\Users\Administrator\Desktop\meiduo_project\meiduo_mall\meiduo_mall'

其路径怎么得来的呢,我们来分析一下代码

代码语言:javascript复制
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  • 其中 __file__ 是指向当前模块
  • os.path.abspath(__file__) 是取当前模块的绝对路径
  • os.path.dirname(os.path.abspath(__file__)) 则是根据当前模块路径获取其所在目录

因此:

代码语言:javascript复制
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

就是获取当前模块的所在目录的上一层目录。

我这里的当前模块是 develop.py,所在目录为 settingssettings 的上一层目录则是 meiduo_mall

因此我们可以通过 BASE_DIR 动态拼接路径,来添加导包路径

代码语言:javascript复制
# 追加子应用导包路径
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))

注册子应用直接写应用名就可以了。

代码语言:javascript复制
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 'meiduo_mall.apps.users'

    'users'
]

导包路径的作用

通过查看导包路径,可以快速的知道项目中各个包该如何的导入。 接手项目时,可以尽快的适应项目导包的方式。 通过追加导包路径,可以简化某些目录复杂的导包方式。

重新导入模块

模块被导入后,import module 不能重新导入模块,重新导入需用 imp 下的 reload

代码语言:javascript复制
from imp import reload

我们还是已上文提到的 aaa.py 模块举例

代码语言:javascript复制
# aaa.py

def test():
	print('追加导包路径成功')

ipython 测验

代码语言:javascript复制
In [21]: import aaa

In [22]: aaa.test()
追加导包路径成功

In [23]:

这时不要关掉 ipython 然后修改 aaa.py 模块的如下

代码语言:javascript复制
# aaa.py

def test():
	print('重新导入模块测试')

然后回到 ipython 中测验

代码语言:javascript复制
# 没修改前
In [21]: import aaa

In [22]: aaa.test()
追加导包路径成功

# 修改之后
In [23]: aaa.test()
追加导包路径成功

In [24]: import aaa

In [25]: aaa.test()
追加导包路径成功

In [26]:

其实 aaa.py 已经修改了,只是当前 ipython 交互器不知道,我们可以再开一个 ipython 交互器验证一下

代码语言:javascript复制
In [3]: import sys

# 我的aaa.py 模块不在当前导包路径下,因此要动态追加一下
In [4]: sys.path.insert(0, 'D:HuiCodePythondemo')

In [5]: import aaa

In [6]: aaa.test()
重新导入模块测试

In [7]:

因此 aaa 模块被导入后,import aaa 不能重新导入模块,重新导入需使用如下方式

代码语言:javascript复制
# 没修改前
In [21]: import aaa

In [22]: aaa.test()
追加导包路径成功

# 修改之后
In [23]: aaa.test()
追加导包路径成功

In [24]: import aaa

In [25]: aaa.test()
追加导包路径成功

In [27]: from imp import reload

In [28]: reload(aaa)
Out[28]: <module 'aaa' from 'D:\Hui\Code\Python\demo\aaa.py'>

In [29]: aaa.test()
重新导入模块测试
 

0 人点赞