上一篇 Python 的模块搜索路径,介绍了 Python 的模块搜索路径,最终起作用的是 sys.path
路径列表。如果要自定义自己的搜索路径,就是要怎么定制 sys.path
的内容。可以简单的用 PYTHONPATH
环境变量前向添加,这儿将要说的是用 .pth
文件的方式。也可由此进一步理解 Python 依赖管理工具,像 virtualenv
等的工作原理。
.pth
文件名是什么,无所谓,Python 只认扩展名。.pth
文件中每行指定一个路径 -- 绝对或相对路径(相对于本 .pth
文件所在的目录),另外还可以空行或 #
开始的注释行,还能有 import
语句,大概只用来校验是否能导入成功,程序代码中还是需要显示的 import 模块。
.pth
文件放在哪里
.pth
文件创建好后应该放到哪里去呢?不是 sys.prefix
指示的位置,也不是 sys.path
中任意一个目录,而是 sys.path
中属于 site.packages 的某一个目录中。可以用
>>> import site
>>> site.getusersitepackages()
>>> site.getsitepackages()
查看到, 看我在 Ubuntu Linux 中看到的内容(为便于阅读,显示列表内容时进行了换行处理)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
>>> import sys, site >>> sys.path ['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/yanbin/.local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.6/dist-packages'] >>> site.getusersitepackages() '/home/yanbin/.local/lib/python3.6/site-packages' >>> site.getsitepackages() ['/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.6/dist-packages'] >>> site.USER_SITE '/home/yanbin/.local/lib/python3.6/site-packages' >>> site.ENABLE_USER_SITE True |
从上面看到 sys.path
包含了 site.getusersitepackages()
和 site.getsitepackages()
的内容,并且 site.getusersitepackages()
在前
'/home/yanbin/.local/lib/python3.6/site-packages',
'/usr/local/lib/python3.6/dist-packages',
'/usr/lib/python3/dist-packages',
'/usr/lib/python3.6/dist-packages'
而我们的 .pth
文件就可以放到以上四个目录中的任意位置,也可以放置多个 .pth
文件,选择那个目录,一个是搜索的先后顺序,还有就是 .pth
中的相对目录会相对于 .pth
所在的位置。
如果是不考虑多用户共享的情况,并且操作不用 sudo
前缀,我们就把 .pth
文件放到 usersitepackages()
指示的目录下,即 /home/yanbin/.local/lib/python3.6/site-packages
。该目录还能直接用如下命令获得得
python3 -m site --user-site
/home/yanbin/.local/lib/python3.6/site-packages
在此位置上创建一个文件,命名为 example.pth
, 内容如下
1 2 |
/home/program/mymath mymodules |
第一行用的绝对目录,第二行为相对目录,相对于 example.pth
所在的目录 /home/yanbin/.local/lib/python3.6/site-packages
。假如以上两个目录
/home/program/mymath
/home/yanbin/.local/lib/python3.6/site-packages/mymodules
都存在的话,进到 Python3 Shell, 再次查看 sys.path
的内容就变成了
1 2 3 4 5 6 7 8 9 10 11 12 |
>>> import sys, site >>> sys.path ['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/yanbin/.local/lib/python3.6/site-packages', '/home/program/mymath', '/home/yanbin/.local/lib/python3.6/site-packages/mymodules', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.6/dist-packages'] |
留意上面的顺序,.pth
放在哪个目录下,它所增加的路径就会跟随在那个 site-packages 后面。效果上相当于
pos = sys.path.index('/home/yanbin/.local/lib/python3.6/site-packages')
sys.path[pos:pos] = ['home/program/mymath', '/home/yanbin/.local/lib/python3.6/site-packages/mymodules']
如果 example.pth
文件放在 /usr/lib/python3/dist-packages
目录中,那么添加的两个路径也就跟在它后面,并且相对路径也是相对它。
Python 会话启动时会对 .pth
中配置的目录存在性进行检测,如果 example.pth
中配置的 /home/program/mymath
不存在,那么 /home/program/mymath
将不会出现在 sys.path
列表中, 相对路径也是如此。
.pth
文件中 import
的作用
再来稍微探讨一下在 .pth
文件中 import
语句的作用,.pth
中除了空行,注释行和目录外,只能有 import
开头的语句。实践中试验了一下,在 .pth
中写上一句
import mymath
并不意味着在程序代码中就能直接用 mymath 模块了,若直接 mymath.pi
使用 mymath
中的属性或方法是会收到
NameErro: name 'mymath' is not defined
的错误,因此尽管在 example.pth
中有 import mymath
, 在程序代码中欲使用 mymath
模块的话还必须加上同样的 import mymath
。
好像 .pth
中的 import mymath
语句没什么用,却又并不属实。比如说在 .pth
中 import mymathxx
不存在的模块,在进入 Python3 会话时就会收到一个错误
➜ ~ python3
Error processing line 4 of /home/yanbin/.local/lib/python3.6/site-packages/example.pth:Traceback (most recent call last):
File "/usr/lib/python3.6/site.py", line 174, in addpackage
exec(line)
File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'mymathxx'Remainder of file ignored
不能加载到模块 mymathxx
, 并且 import mymathxx
后的内容被忽略掉,仍能进入 Python3 的 Shell。
所以粗略的感觉 .pth
中的 import
的语句只是用来校验想要加载的模块是否真的存在。当有任何欲加载的模块不能导入的话应当给予重视。
pipenv
和 virtualenv
中的路径初探
pipenv
和 virtualenv
环境中的 site
没有了 site.getsitepackages()
和 site.getusersitepackages()
这两个方法了。
pipenv
pipenv shell
,然后再进入 python3
或者用 pipenv run python3 main.py
来查看 sys.path
的内容,类似如下:
1 2 3 4 5 6 |
'/home/yanbin/myproject' '/home/yanbin/.local/share/virtualenvs/myproject-iMKbKEIX/lib/python36.zip' '/home/yanbin/.local/share/virtualenvs/myproject-iMKbKEIX/lib/python3.6' '/home/yanbin/.local/share/virtualenvs/myproject-iMKbKEIX/lib/python3.6/lib-dynload' '/usr/lib/python3.6' '/home/yanbin/.local/share/virtualenvs/myproject-iMKbKEIX/lib/python3.6/site-packages' |
virtualenv
如果是一个 virtualenv
项目的话,它目录下有自己独套班子,bin
, include
, lib
目录,bin
下有自己的 python3, pip3 等命令。执行项目自己的 python3
查看 sys.path
内容大致如下:
1 2 3 4 5 6 |
['', '/home/yanbin/myproject/lib/python36.zip', '/home/yanbin/myproject/lib/python3.6', '/home/yanbin/myproject/lib/python3.6/lib-dynload', '/usr/lib/python3.6', '/home/yanbin/myproject/lib/python3.6/site-packages'] |
几乎所有的依赖都能在项目目录下找到(除 /usr/lib/python3.6 外),不用跑到用户目录或 Python 公共的 site-packages 目录去找。因此 virtualenv
项目才是一个独立可部署的,打包,拷贝,解压后运行自己的 bin/Python3 命令即可。
链接:
[…] 用 .pth 文件附加 Python 模块搜索路径 | 隔叶黄莺 Yanbin Blog - 软件编程实践 […]
[…] 用 .pth 文件附加 Python 模块搜索路径 | 隔叶黄莺 Yanbin Blog - 软件编程实践 […]
[…] 用 .pth 文件附加 Python 模块搜索路径 | 隔叶黄莺 Yanbin Blog – 软件编程实践 […]
[…] /Users/yanbin/my-package 见 用 .pth 文件附加 Python 模块搜索路径 […]