python: lib/site-packages/site.py与lib/site.py的关系

11 投票
1 回答
3780 浏览
提问于 2025-04-20 08:36

由于我解决了一个特定的问题,今天大部分时间我都在研究site.py是怎么工作的。不过有一点我不太明白。

根据我的理解,当Python被加载时,首先会运行lib/python2.7/site-packages/site.py。这个文件会查看PYTHONPATH,寻找lib/python2.7/site.py,然后把它导入。这个文件里有一个addsitedir的方法,它不仅会把一个路径添加到sys.path中,还会处理这个路径下的*.pth文件。到这一步时,lib/python2.7/site.py里的main()会被执行,addsitedir会在site-packages和用户的site-packages上运行。

接下来就是奇怪的地方了。我们又回到lib/python2.7/site-packages/site.py,它会遍历pythonpath中的每一个路径,并对每个路径运行addsitedir。我觉得这很奇怪,主要有两个原因:

  1. addsitedirlib/python2.7/site-packages上被执行了两次。
  2. 这本身并不算太糟糕(因为同一个路径不会被添加到sys.path两次),但似乎lib/python2.7/site.py有一个机制,允许用户通过实现一个usercustomize模块来操控sys.path(嘿,这在文档里都有提到)。显然,当你实现这样的机制时,你希望用户的代码最后执行,这样他就能控制添加到sys.path的所有内容。但在这里并不是这样(我很沮丧地发现了这一点)。很可能第二次对lib/python2.7/site-packages的调用会覆盖usercustomize中做的所有事情。

我知道这很糟糕,但我在addsitedir中加了一个打印语句,打印它接收到的路径,以便我能展示发生了什么。这些是处理过的路径:

/home/user/.local/lib/python2.7/site-packages #lib/python2.7/site.py
/home/user/py/lib/python2.7/site-packages     #lib/python2.7/site.py
#This is where your usercustomize runs
#Followin calls are from lib/python2.7/site-packages/site.py
/home/user/py/lib/python2.7/site-packages/numpy-1.9.0-py2.7-linux-x86_64.egg
/home/user/Develop/Python/myproject
/home/user/lmfit-0.7.2
/home/user/py/lib/python2.7/site-packages #NOTE: this runs a second time

那么我在这里想问什么呢? :)

A. 我希望能得到一些关于为什么需要第二次调用site-packages的见解。

B. usercustomize是否真的像我认为的那样受到限制?考虑到这一点,你会如何理论上实现一个移除路径的功能,从sys.path中?


请求的调试输出:

:genie39:~ ;-) python2.7 -v
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
# /home/user/py/lib/python2.7/site-packages/site.pyc matches /home/user/py/lib/python2.7/site-packages/site.py
import site # precompiled from /home/user/py/lib/python2.7/site-packages/site.pyc
# /home/user/py/lib/python2.7/os.pyc matches /home/user/py/lib/python2.7/os.py
import os # precompiled from /home/user/py/lib/python2.7/os.pyc
import errno # builtin
import posix # builtin
# /home/user/py/lib/python2.7/posixpath.pyc matches /home/user/py/lib/python2.7/posixpath.py
import posixpath # precompiled from /home/user/py/lib/python2.7/posixpath.pyc
# /home/user/py/lib/python2.7/stat.pyc matches /home/user/py/lib/python2.7/stat.py
import stat # precompiled from /home/user/py/lib/python2.7/stat.pyc
# /home/user/py/lib/python2.7/genericpath.pyc matches /home/user/py/lib/python2.7/genericpath.py
import genericpath # precompiled from /home/user/py/lib/python2.7/genericpath.pyc
# /home/user/py/lib/python2.7/warnings.pyc matches /home/user/py/lib/python2.7/warnings.py
import warnings # precompiled from /home/user/py/lib/python2.7/warnings.pyc
# /home/user/py/lib/python2.7/linecache.pyc matches /home/user/py/lib/python2.7/linecache.py
import linecache # precompiled from /home/user/py/lib/python2.7/linecache.pyc
# /home/user/py/lib/python2.7/types.pyc matches /home/user/py/lib/python2.7/types.py
import types # precompiled from /home/user/py/lib/python2.7/types.pyc
# /home/user/py/lib/python2.7/UserDict.pyc matches /home/user/py/lib/python2.7/UserDict.py
import UserDict # precompiled from /home/user/py/lib/python2.7/UserDict.pyc
# /home/user/py/lib/python2.7/_abcoll.pyc matches /home/user/py/lib/python2.7/_abcoll.py
import _abcoll # precompiled from /home/user/py/lib/python2.7/_abcoll.pyc
# /home/user/py/lib/python2.7/abc.pyc matches /home/user/py/lib/python2.7/abc.py
import abc # precompiled from /home/user/py/lib/python2.7/abc.pyc
# /home/user/py/lib/python2.7/_weakrefset.pyc matches /home/user/py/lib/python2.7/_weakrefset.py
import _weakrefset # precompiled from /home/user/py/lib/python2.7/_weakrefset.pyc
import _weakref # builtin
# /home/user/py/lib/python2.7/copy_reg.pyc matches /home/user/py/lib/python2.7/copy_reg.py
import copy_reg # precompiled from /home/user/py/lib/python2.7/copy_reg.pyc
import imp # builtin
# /home/user/py/lib/python2.7/site.pyc matches /home/user/py/lib/python2.7/site.py
import site # precompiled from /home/user/py/lib/python2.7/site.pyc
# /home/user/py/lib/python2.7/traceback.pyc matches /home/user/py/lib/python2.7/traceback.py
import traceback # precompiled from /home/user/py/lib/python2.7/traceback.pyc
# /home/user/py/lib/python2.7/sysconfig.pyc matches /home/user/py/lib/python2.7/sysconfig.py
import sysconfig # precompiled from /home/user/py/lib/python2.7/sysconfig.pyc
# /home/user/py/lib/python2.7/re.pyc matches /home/user/py/lib/python2.7/re.py
import re # precompiled from /home/user/py/lib/python2.7/re.pyc
# /home/user/py/lib/python2.7/sre_compile.pyc matches /home/user/py/lib/python2.7/sre_compile.py
import sre_compile # precompiled from /home/user/py/lib/python2.7/sre_compile.pyc
import _sre # builtin
# /home/user/py/lib/python2.7/sre_parse.pyc matches /home/user/py/lib/python2.7/sre_parse.py
import sre_parse # precompiled from /home/user/py/lib/python2.7/sre_parse.pyc
# /home/user/py/lib/python2.7/sre_constants.pyc matches /home/user/py/lib/python2.7/sre_constants.py
import sre_constants # precompiled from /home/user/py/lib/python2.7/sre_constants.pyc
# /home/user/py/lib/python2.7/_sysconfigdata.pyc matches /home/user/py/lib/python2.7/_sysconfigdata.py
import _sysconfigdata # precompiled from /home/user/py/lib/python2.7/_sysconfigdata.pyc
# zipimport: found 604 names in /home/user/py/lib/python2.7/site-packages/pytz-2014.7-py2.7.egg
# zipimport: found 20 names in /home/user/py/lib/python2.7/site-packages/hashlib-20081119-py2.7-linux-x86_64.egg
# zipimport: found 40 names in /home/user/py/lib/python2.7/site-packages/pysqlite-2.6.3-py2.7-linux-x86_64.egg
# zipimport: found 7 names in /home/user/py/lib/python2.7/site-packages/mock-1.0.1-py2.7.egg
import encodings # directory /home/user/py/lib/python2.7/encodings
# /home/user/py/lib/python2.7/encodings/__init__.pyc matches /home/user/py/lib/python2.7/encodings/__init__.py
import encodings # precompiled from /home/user/py/lib/python2.7/encodings/__init__.pyc
# /home/user/py/lib/python2.7/codecs.pyc matches /home/user/py/lib/python2.7/codecs.py
import codecs # precompiled from /home/user/py/lib/python2.7/codecs.pyc
import _codecs # builtin
# /home/user/py/lib/python2.7/encodings/aliases.pyc matches /home/user/py/lib/python2.7/encodings/aliases.py
import encodings.aliases # precompiled from /home/user/py/lib/python2.7/encodings/aliases.pyc
# /home/user/py/lib/python2.7/encodings/utf_8.pyc matches /home/user/py/lib/python2.7/encodings/utf_8.py
import encodings.utf_8 # precompiled from /home/user/py/lib/python2.7/encodings/utf_8.pyc
Python 2.7.8 (default, Sep  7 2014, 12:14:33) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
dlopen("/home/user/py/lib/python2.7/site-packages/readline-6.2.4.1-py2.7-linux-x86_64.egg/readline.so", 2);
import readline # dynamically loaded from /home/user/py/lib/python2.7/site-packages/readline-6.2.4.1-py2.7-linux-x86_64.egg/readline.so
>>> 

运行python -vv的输出在这里

1 个回答

9

lib/python2.7/site-packages/site.py 这个文件通常不会被加载。这是因为它的任务是将 site-packages 的路径添加到 sys.path 中,而 site-packages 中的 site.py 是不可见的。如果你在 site-packages 中找到了 site.py,那就是个错误,那里不应该有这个文件。

在没有修补的 Python 中,会发生以下情况:

  • Python 启动时,sys.path 的内容很有限。site-packages 不在这个列表中,除非你设置了包含它的 PYTHONPATH 变量。
  • 当 Python 导入 site.py 时,它会导入 sys.path 中第一个列出的那个。
  • 找到并加载 lib/python2.7/site.py
  • site.pysite-packages 添加到 sys.path 中。

就这样,没有进一步的 site.py 模块被加载。即使你尝试加载,也只会找到已经 被导入 的模块;sys.modules['site'] 存在,并保存了从 lib/python2.7/site.py 加载的对象。

不过,你的安装中有一个旧版本的 setuptools,它包含一个特殊版本的 site.py,这个 easy_install 命令会将其安装到 site-packages 中,如果那里还没有的话。它会通过明确扫描原始的 sys.path 来加载原始的 site.py,忽略任何 PYTHONPATH 提供的路径,并手动加载原始的 site.py 模块,使用imp.find_module()imp.load_module() 这些低级函数,从而绕过正常的模块缓存。

它的目的是改变 sys.path 的顺序,使得 PYTHONPATH 列出的 .pth 文件具有更高的优先级,具体可以查看添加补丁的原始提交

注意:这个版本包含一个修改过的 'site.py',以支持处理在 site-packages 之前的目录中的 .pth 文件。

这个补丁在更新的 setuptools 版本中已经完全移除,早在 2006 年的原始 setuptools 中就已经如此。

所以,要么你的 Linux 发行版已经设置好将 lib/python2.7/site-packages 添加到你的 PYTHONPATH 中,要么你的 shell 为你设置了这个,或者你的 Python 已经被修补以包含它,并且 你在 site-packages 中有旧的 setuptools '补丁'。

删除这个文件是完全安全的。

撰写回答