如何检查模块/库/包是否是Python标准库的一部分?

21 投票
3 回答
14238 浏览
提问于 2025-04-17 20:55

我用pip安装了很多库、模块和包,现在我搞不清楚哪些是Python自带的,哪些是我自己安装的。这就导致我的代码在我的电脑上能运行,但在其他地方却不行。

我该怎么检查我在代码中导入的模块、库或包是否是Python自带的呢?

假设我是在一台安装了所有外部库、模块和包的机器上进行检查,否则我可以在没有这些库的机器上用try-except来尝试导入。

比如,我确定这些导入在我的机器上能正常工作,但在只有基础Python安装的机器上就会出错:

from bs4 import BeautifulSoup
import nltk
import PIL
import gensim

3 个回答

2

@Bakuriu的回答对我帮助很大。唯一的问题是,如果你想检查某个特定模块是否是标准库,但它已经被导入了。在这种情况下,sys.modules中只会有这个模块的记录,所以即使sys.path被清空,导入仍然会成功:

In [1]: import sys

In [2]: import virtualenv

In [3]: sys.path = []

In [4]: try:
    __import__('virtualenv')
except ImportError:
    print(False)
else:
    print(True)
   ...:
True

In [1]: import sys

In [2]: sys.path = []

In [3]: try:
    __import__('virtualenv')
except ImportError:
    print(False)
else:
    print(True)
   ...:
False

我想出了以下解决方案,这个方法在Python2和Python3中都能用:

from __future__ import unicode_literals, print_function
import sys
from contextlib import contextmanager
from importlib import import_module


@contextmanager
def ignore_site_packages_paths():
    paths = sys.path
    # remove all third-party paths
    # so that only stdlib imports will succeed
    sys.path = list(filter(
        None,
        filter(lambda i: 'site-packages' not in i, sys.path)
    ))
    yield
    sys.path = paths


def is_std_lib(module):
    if module in sys.builtin_module_names:
        return True

    with ignore_site_packages_paths():
        imported_module = sys.modules.pop(module, None)
        try:
            import_module(module)
        except ImportError:
            return False
        else:
            return True
        finally:
            if imported_module:
                sys.modules[module] = imported_module

你可以在这里查看源代码

5

标准库在Python的文档中有详细说明。你可以直接在那儿搜索,或者把模块名称放到一个列表里,然后用程序来检查。

另外,在Python 3.4中有一个新的隔离模式,可以让你忽略一些用户自定义的库路径。在之前的Python版本中,你可以用-s来忽略用户环境,-E来忽略系统定义的变量。

在Python 2中,检查一个模块是否属于标准库的一个简单方法是清空sys.path

>>> import sys
>>> sys.path = []
>>> import numpy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named numpy
>>> import traceback
>>> import os
>>> import re

不过在Python 3.3及以上版本中,这个方法就不管用了:

>>> import sys
>>> sys.path = []
>>> import traceback
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'traceback'
[...]

这是因为从Python 3.3开始,导入机制发生了变化,导入标准库和导入其他模块使用的是相同的方式(具体可以查看文档)。

在Python 3.3中,确保只导入标准库的方法是只将标准库的路径添加到sys.path中,例如:

>>> import os, sys, traceback
>>> lib_path = os.path.dirname(traceback.__file__)
>>> sys.path = [lib_path]
>>> import traceback
>>> import re
>>> import numpy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'numpy'

我使用traceback模块来获取库路径,因为这个方法在任何系统上都能工作。

对于内置模块,它们是标准库模块的一个子集,你可以查看sys.builtin_module_names

9

你需要检查所有已经导入的模块,看看有没有哪个模块是在标准库之外的。

下面这个脚本虽然不是完全可靠,但可以作为一个起点:

import sys
import os

external = set()
exempt = set()
paths = (os.path.abspath(p) for p in sys.path)
stdlib = {p for p in paths
          if p.startswith((sys.prefix, sys.real_prefix)) 
          and 'site-packages' not in p}
for name, module in sorted(sys.modules.items()):
    if not module or name in sys.builtin_module_names or not hasattr(module, '__file__'):
        # an import sentinel, built-in module or not a real module, really
        exempt.add(name)
        continue

    fname = module.__file__
    if fname.endswith(('__init__.py', '__init__.pyc', '__init__.pyo')):
        fname = os.path.dirname(fname)

    if os.path.dirname(fname) in stdlib:
        # stdlib path, skip
        exempt.add(name)
        continue

    parts = name.split('.')
    for i, part in enumerate(parts):
        partial = '.'.join(parts[:i] + [part])
        if partial in external or partial in exempt:
            # already listed or exempted
            break
        if partial in sys.modules and sys.modules[partial]:
            # just list the parent name and be done with it
            external.add(partial)
            break

for name in external:
    print name, sys.modules[name].__file__

把这个放在一个新的模块里,在你的脚本中所有导入之后再导入它,它会打印出所有它认为不是标准库的一部分的模块。

撰写回答