如何检查模块/库/包是否是Python标准库的一部分?
我用pip安装了很多库、模块和包,现在我搞不清楚哪些是Python自带的,哪些是我自己安装的。这就导致我的代码在我的电脑上能运行,但在其他地方却不行。
我该怎么检查我在代码中导入的模块、库或包是否是Python自带的呢?
假设我是在一台安装了所有外部库、模块和包的机器上进行检查,否则我可以在没有这些库的机器上用try-except来尝试导入。
比如,我确定这些导入在我的机器上能正常工作,但在只有基础Python安装的机器上就会出错:
from bs4 import BeautifulSoup
import nltk
import PIL
import gensim
3 个回答
@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
你可以在这里查看源代码
标准库在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
。
你需要检查所有已经导入的模块,看看有没有哪个模块是在标准库之外的。
下面这个脚本虽然不是完全可靠,但可以作为一个起点:
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__
把这个放在一个新的模块里,在你的脚本中所有导入之后再导入它,它会打印出所有它认为不是标准库的一部分的模块。