python路径和导入顺序

时间:2012-03-06 15:28:04

标签: python

好的,所以我真的想要做到这一点,因为我在生成一些大的py2app / py2exe包时会继续遇到它。所以我的包中包含很多模块/包,这些模块/包也可能在用户站点包/默认位置(如果用户有python发行版)但我希望我的分布式包在从我的发行版运行时生效。

现在从我读过here PYTHONPATH 应该是当前目录之后添加到sys.path的第一件事,但是我在我的机器上测试的是不是这种情况,$site-packages$/easy-install.pth中定义的所有文件夹都优先于此。

有人可以请给我一些关于这个导入顺序的更深入的解释//帮我找到一种方法来设置环境变量,使我分发的包优先于默认安装的包。到目前为止,我的尝试是,例如在Mac-OS py2app上,在我的入口点脚本中:

 os.environ['PYTHONPATH'] = DATA_PATH + ':'
 os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + os.path.join(DATA_PATH
                                                            , 'lib') + ':'
 os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + os.path.join(
                                DATA_PATH, 'lib', 'python2.7', 'site-packages') + ':'
 os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + os.path.join(
                          DATA_PATH, 'lib', 'python2.7', 'site-packages.zip')

这基本上是py2app生成的包的结构。然后我就是:

 SERVER = subprocess.Popen([PYTHON_EXE_PATH, '-m', 'bin.rpserver'
                            , cfg.RPC_SERVER_IP, cfg.RPC_SERVER_PORT],
                            shell=False, stdin=IN_FILE, stdout=OUT_FILE, 
                            stderr=ERR_FILE)

这里PYTHON_EXE_PATH是python应用程序添加到包中的python exe的路径。现在,这在没有安装python的机器上运行良好。但是,当python发布已经存在时,它们的站点包优先。

4 个答案:

答案 0 :(得分:16)

Python按顺序搜索sys.path中的路径(请参阅http://docs.python.org/tutorial/modules.html#the-module-search-path)。 easy_install直接更改此列表(请参阅easy-install.pth文件中的最后一行):

import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

这基本上取了添加的任何目录,并将它们插入列表的开头。

另见Eggs in path before PYTHONPATH environment variable

答案 1 :(得分:0)

导入模块后,python首先从sys.modules目录列表中搜索。 如果找不到,则从sys.path目录列表中搜索。在您的操作系统上可能还有其他python搜索列表

import time , sys
print (sys.modules)
print (sys.path)

输出是目录列表:

{... , ... , .....}
['C:\\Users\\****', 'C:\\****', ....']

time模块是按照sys.modulessys.path列表的顺序导入的。

答案 2 :(得分:0)

即使以上关于口译员扫描sys.path的顺序的答案是正确的,也优先考虑例如如果site-packages变量中没有完整的用户路径,则通过PYTHONPATH部署的软件包的用户文件路径可能会失败。

例如,假设您具有以下名称空间包结构:

/opt/repo_root
  - project  # this is the base package that brigns structure to the namespace hierarchy
  - my_pkg
  - my_pkg-core
  - my_pkg-gui
  - my_pkg-helpers
  - my_pkg-helpers-time_sync

以上软件包都具有内部所需的结构和元数据,以便可以通过conda进行部署,并且这些软件包也都已安装。因此,我可以打开python shell并键入:

>>> from project.my_pkg.helpers import time_sync
>>> print(time_sync.__file__)

/python/interpreter/path/lib/python3.6/site_packages/project/my_pkg/helpers/time_sync/__init__.py

将在python解释器的site-packages子文件夹中返回一些路径。如果我手动将要导入的软件包添加到PYTHONPATH或什至sys.path,则什么都不会改变。

>>> import os

>>> # joining separator ":" for Unix, ";" for NT
>>> os.environ['PYTHONPATH'] = ":".join(os.environ['PYTHONPATH'], "/opt/repo_root/my_pkg-helpers-time_sync")

>>> from project.my_pkg.helpers import time_sync
>>> print(time_sync.__file__)

/python/interpreter/path/lib/python3.6/site_packages/project/my_pkg/helpers/time_sync/__init__.py

仍然返回该软件包已从site-packages导入。您需要将路径的整个层次结构都包含在PYTHONPATH中,就像它是传统的python软件包一样,然后它才能按预期工作:

>>> import os

>>> # joining separator ":" for Unix, ";" for NT
>>> os.environ['PYTHONPATH'] = ":".join(
... os.environ['PYTHONPATH'],
... "/opt/repo_root",
... "/opt/repo_root/project",
... "/opt/repo_root/project/my_pkg",
... "/opt/repo_root/project/my_pkg-helpers",
... "/opt/repo_root/project/my_pkg-helpers-time_sync"
... )

>>> from project.my_pkg.helpers import time_sync
>>> print(time_sync.__file__)

/opt/project/my_pkg/helpers/time_sync/__init__.py

答案 3 :(得分:-1)

此页面是Google对“ Python导入顺序”的最高评价,因此,这里的解释希望会更清楚:

这两个页​​面都说明,import的顺序是:

  1. 内置python模块。
  2. sys.path项。
  3. 与安装有关的默认位置。

并且如sys.path文档页面所述,其填充如下:

  1. 第一个条目是python开头的文件的完整路径(因此/someplace/on/disk/> $ python /path/to/the/run.py表示第一个路径为/path/to/the/,如果您位于/path/to/> $ python the/run.py(仍将始终设置为目录的FULL PATH),或者如果python是在没有文件aka交互模式的情况下启动的,则它将是一个空字符串(空字符串表示“当前工作目录”用于python进程”)。换句话说,Python假设您启动的文件希望能够相对导入与您启动python的文件位于同一位置的package/-foldersblah.py模块。
  2. sys.path环境变量中填充了PYTHONPATH中的其他条目。基本上是您的全局pip文件夹。

因此,基本上:是的,您可以放心,Python会在任何全局安装的pip东西之前先找到本地的软件包文件夹和模块文件。

下面是一个进一步解释的示例:

myproject/ # <-- This is not a package (no __init__.py file).
  modules/ # <-- This is a package (has an __init__.py file).
    __init__.py
    foo.py
  run.py
  second.py

executed with: python /path/to/the/run.py
will cause sys.path[0] to be "/path/to/the/"

run.py contents:
import modules.foo as foo # will import "/path/to/the/" + "modules/foo.py"
import second # will import "/path/to/the/" + "second.py"

second.py contents:
import modules.foo as foo # will import "/path/to/the/" + "modules/foo.py"