我们已经准备好了,你呢?

2024我们与您携手共赢,为您的企业形象保驾护航!

前言

我写这篇文章是因为我最近看到社区里经常被问到的几个问题:

授人以鱼不如授人以渔。要解决这种问题,你需要知道如何找到包。希望这篇文章对你有帮助。(主要原因是下次有人问我的时候,我可以把链接发给他们,然后打他们脸,哈哈)

如何查找包

现在,你的电脑上可能存在多个虚拟环境,这可能会导致你在安装包时忘记注意安装包的路径。首先我们来解决寻找包的问题。这个问题回答起来很简单,但是很多人不知道其中的原理。如果你的解释器的路径是 $/bin/,那么当你用这个解释器启动交互式环境或者运行脚本时,它会默认到以下位置:

$/lib(标准库路径)$/lib/.Y/site-(第三方库路径,XY为对应的主版本号和次版本号,如3.7、2.6)当前工作目录(pwd命令的结果)

如果在Linux上使用默认,$就是/usr。如果使用默认选项自己编译,$就是/usr/local。从上面第二项可以看出,不同版本的第三方库路径不一样。如果从3.6升级到3.7,之前安装的第三方库就不能用了。当然你也可以把整个文件夹复制过来,大部分情况下不会有问题。

几个有用的功能

例子:

>>> import sys
>>> sys.executable
'/home/frostming/.pyenv/versions/3.7.2/bin/python'
>>> sys.path
['', '/home/frostming/.pyenv/versions/3.7.2/lib/python37.zip', '/home/frostming/.pyenv/versions/3.7.2/lib/python3.7', '/home/frostming/.pyenv/versions/3.7.2/lib/python3.7/lib-dynload', '/home/frostming/.local/lib/python3.7/site-packages', '/mnt/d/Workspace/pipenv', '/home/frostming/.pyenv/versions/3.7.2/lib/python3.7/site-packages']
>>> sys.prefix
'/home/frostming/.pyenv/versions/3.7.2'

使用环境变量添加搜索路径

如果您的包的路径不存在于上面列出的搜索路径列表中,您可以将路径添加到环境变量中,多个路径用:分隔(使用;)。

但注意不要在 中添加不同版本包的路径,比如 =/home//.local/lib/.7/site-,因为 中的路径优先于默认搜索路径,如果使用 3 的话会有兼容性问题。其实 中最好不要有任何带有 site- 的路径。

顺便说一下,PATH 是用来查找可执行程序的搜索路径。如果你在终端中运行一个命令,系统会逐个扫描 PATH 中的路径,看它们是否存在于该路径中。所以如果它说找不到程序或无法识别命令,那么你就得看看该路径是否已经添加到 PATH 中。

如何安装该软件包

现在安装包基本都是用pip,就算用pip,底层还是pip,适用所有人,如果你没安装过pip,可以参考这里,如果你已经安装过pip,但是还是不能使用pip命令,可以参考上一节。

有两种方法可以运行 pip:

第一种方法和第二种方法类似,区别在于第一种方法使用的解释器写在pip文件中。一般如果你的pip路径是$/bin/pip,那么该路径对应的就是$/bin/。如果你使用的是Unix系统,cat $(which pip)第一行就包含解释器路径。第二种方法明确指定了位置。这个规则适用于所有可执行程序。流程如下图所示。

1

然后,无需任何自定义配置,使用 pip 安装包就会自动安装到 $/lib/.Y/site- 下(这个 $ 是从上一段得到的),可执行程序会安装到 $/bin 下,如果需要直接在命令行运行的话,记得添加到 PATH 中。

在 pip 虚拟环境中更改安装位置的选项

虚拟环境的目的是为了隔离不同项目的依赖包,并将它们安装在不同的路径下,防止依赖冲突。理解了如何安装包的机制之后,理解虚拟环境(venv 模块)的原理就不难了。其实运行 myenv 会把新的解释器复制到 myenv/bin 中,并建立 myenv/lib、myenv/lib/.Y/site- 等目录(复制时并没有用到 venv 模块,但效果基本一样)。执行 myenv/bin/ 之后会把 myenv/bin 放到 PATH 最前面,这样就会优先搜索复制过来的解释器。这样后面安装包的时候,$ 就是 myenv,从而实现了安装路径的隔离。

脚本执行模式对搜索路径的影响

此部分于 2021/1/21 添加

从上面的介绍中,我们可以知道一个包是否能被找到,最直接的原因是sys.path,而更进一步的原因则是sys的路径。程序写好之后,我们总是需要运行它,不同的运行方式可能会影响sys.path,导致不同的行为。下面我们就来讨论一下这个问题。

假设你的包结构如下

.
├── main.py
└── my_package
    ├── __init__.py
    ├── a.py
    └── b.py

main.py 的内容:

import my_package.b

b.py文件内容很简单:

import sys
print("I'm b")
print(sys.path)

现在在与main.py相同的目录中执行

$ python main.py
I'm b
['/home/frostming/test_path', ...]  # 省略的路径是共同的,与讨论的问题无关
$ python my_package/b.py
I'm b
['/home/frostming/test_path/my_package', ...]

xxx.py 的运行方式叫直接运行,此时会指定文件中的值,这就是 IDE 中“运行文件”和“运行脚本”的使用方式,可以看到 sys.path 的第一个值就是脚本文件所在的目录,随着脚本路径的变化而变化,记住我们一直都在目录 /home// 中执行测试。

好的,那么如果我们需要在b.py中导入a.py,并且a.py包含一行简单的print(“I'm a”),我们应该在b.py脚本中写什么?

简单!,好的,再次运行上面的测试

$ python main.py
ModuleNotFoundError: No module named 'a'
$ python my_package/b.py
I'm a
I'm b
['/home/frostming/test_path/my_package', ...]

第一个测试失败。如果你看过前面的内容,就会知道这个错误是预料之中的——sys.path 没有 a.py 所在的目录 /home///,所以当然找不到 a。

我们将其改为 from a 并且不进行测试,因为基于同样的分析,我们可以预测第一个会运行良好,但第二个会失败。请注意,由于 b 在包中,我们可以使用相对导入,并且 from . a 的写法与 from a 相同。

那么有没有办法让我两次运行都不报错呢?有的。要知道一个项目中的入口点是有限的,实际上不会同时在顶层和子目录中都有可执行代码。我们应该把主要的操作逻辑放在 main.py 中(不一定是这个名字,比如项目就是 .py)。如果确实需要运行子目录中的脚本代码,应该使用 -m,并且 b.py 中 a 的语句应该是 from a。我们来看看运行效果:

$ python main.py  # 和 python -m main 效果一样
I'm a
I'm b
['/home/frostming/test_path', ...]
$ python -m my_package.b
I'm a
I'm b
['/home/frostming/test_path', ...]

可以看到这两次运行的 sys.path 内容是一致的,它的第一个值就是当前运行所在的目录。这种运行方式称为以模块模式运行,并且 -m 后面的参数是模块名(以 . 分隔),而不是路径名。因为这种统一性,你项目中的所有导入都可以用同样的方式定义,无论它们在哪个脚本中。这也是为什么官方文档建议所有导入名称都采用 myapp..users 的形式。

另外,作为模块运行时,传递的模块名的每个父模块(或包)都会作为模块运行,这意味着你可以在模块中使用相对导入(直接运行时则不行),传递的模块的值会设置为 ,你仍然可以应用 if == "": 判断。如果 -m 传递的模块是包,那么包目录中的 .py 脚本(如果存在)会被执行,脚本的值为 。

总结

这里可以看到,包路径搜索最重要的是 $path 前缀,而这个值是从使用的解释器路径中得出的。所以要找到包路径,你只需要知道解释器路径。如果遇到包路径发生变化,你只需要通过正确的 PATH 设置指定你想要的解释器即可。

现在让我们回到开头的三个问题。你解决了吗?在评论部分写下您的故障排除步骤或解决方案。

本文示例均采用Unix路径约定,如果是系统,应做适当修改,如$/bin应为$/

二维码
扫一扫在手机端查看

本文链接:https://by928.com/3359.html     转载请注明出处和本文链接!请遵守 《网站协议》
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。

项目经理在线

我们已经准备好了,你呢?

2020我们与您携手共赢,为您的企业形象保驾护航!

在线客服
联系方式

热线电话

13761152229

上班时间

周一到周五

公司电话

二维码
微信
线