确定给定的Python模块是否为内置模块
我正在处理一些模块的解析和检查,但我不想解析内置模块。现在,内置模块没有像types.BuiltinFunctionType
那样的特殊类型,那我该怎么做呢?
>>> import CornedBeef
>>> CornedBeef
<module 'CornedBeef' from '/meatish/CornedBeef.pyc'>
>>> CornedBeef.__file__
'/meatish/CornedBeef.pyc'
>>> del CornedBeef.__file__
>>> CornedBeef
<module 'CornedBeef' (built-in)>
根据Python的说法,如果一个模块没有__file__
这个属性,那它显然就是内置模块。这是不是意味着可以通过hasattr(SomeModule, '__file__')
来检查一个模块是否是内置的?当然,直接del SomeModule.__file__
并不常见,但有没有更可靠的方法来判断一个模块是否是内置模块呢?
4 个回答
你可以使用 imp.is_builtin
来检查一个模块名是否是内置模块,但我想不出有什么可靠的方法可以深入查看一个模块对象。
你也可以尝试以下方法:
>>> import imp
>>> f, path, desc = imp.find_module("sys")
>>> desc
('', '', 6)
>>> desc[2] == imp.C_BUILTIN
True
如果你只是简单地考虑builtins
,那么被接受的答案显然是正确的。
在我的情况下,我也在寻找标准库,也就是指一份包含所有可以导入的模块的列表,这些模块是随特定的Python版本一起提供的。关于这个问题已经问过好几次,但我找不到一个包含我想要的所有内容的答案。
我的使用场景是想要把任意的x
放在Python的import x
语句中,判断它属于以下哪一类:
- 包含在Python的标准库和内置模块中
- 作为第三方模块安装的
- 都不是
这个方法适用于虚拟环境或全局安装。它会查询正在运行脚本的Python版本的分发情况。最后一部分会超出虚拟环境的范围,但我认为这是预期的行为。
# You may need to use setuptools.distutils depending on Python distribution (from setuptools import distutils)
import distutils
import glob
import os
import pkgutil
import sys
def get_python_library():
# Get list of the loaded source modules on sys.path.
modules = {
module
for _, module, package in list(pkgutil.iter_modules())
if package is False
}
# Glob all the 'top_level.txt' files installed under site-packages.
site_packages = glob.iglob(os.path.join(os.path.dirname(os.__file__)
+ '/site-packages', '*-info', 'top_level.txt'))
# Read the files for the import names and remove them from the modules list.
modules -= {open(txt).read().strip() for txt in site_packages}
# Get the system packages.
system_modules = set(sys.builtin_module_names)
# Get the just the top-level packages from the python install.
python_root = distutils.sysconfig.get_python_lib(standard_lib=True)
_, top_level_libs, _ = list(os.walk(python_root))[0]
return sorted(top_level_libs + list(modules | system_modules))
返回结果
一个排序后的导入模块列表:[..., 'imaplib', 'imghdr', 'imp', 'importlib', 'imputil', 'inspect', 'io', ...]
解释:
我把它分成几个部分,这样每一组的必要性就能清楚了。
modules
- 调用
pkgutil.iter_modules
会扫描所有加载的模块,并返回一个包含(模块加载器, 名称, 是否是包)
的生成器。 - 我把它变成一个集合,并过滤掉包,因为我们这里只关心源模块。
- 调用
site_packages
- 获取常规的site-packages目录下所有已安装包的列表,并从
modules
列表中移除它们。这大致对应于第三方依赖。 - 这是我最难搞定的部分。很多方法几乎能工作,比如
pip.get_installed_distributions
或site
。但pip
返回的模块名称是它们在PyPi上的样子,而不是在源文件中导入时的样子。有些特殊的包会漏掉,比如:requests-futures
在导入时是requests_futures
。colors
,在PyPi上其实是ansicolors
,这让任何合理的判断都变得复杂。
- 我相信有些使用频率低的模块没有在它们的包中包含
top_level.txt
。但这覆盖了我所有的使用场景,似乎在所有正确配置的情况下都能工作。
- 获取常规的site-packages目录下所有已安装包的列表,并从
system_modules
- 如果你不明确请求这些模块,你是得不到系统模块的,比如
sys
、gc
、errno
和其他一些可选模块。
- 如果你不明确请求这些模块,你是得不到系统模块的,比如
top_level_libs
- 调用
distutils.sysconfig.get_python_lib(standard_lib=True)
会返回平台无关的标准库的顶层目录。 - 这些模块容易被忽视,因为它们可能不在与其他模块相同的Python路径下。如果你在OSX上并运行虚拟环境,这些模块实际上会从系统安装中导入。这些模块包括
email
、logging
、xml
等。
- 调用
结论
在我的2013款MacBookPro上,我找到了403个python2.7
安装的模块。
>>> print(sys.version)
2.7.10 (default, Jul 13 2015, 12:05:58)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)]
>>> print(sys.hexversion)
34015984
>>> python_stdlib = get_python_libirary()
>>> len(python_stdlib)
403
我在这里放了一个代码和输出的链接。如果你觉得我漏掉了某个类或者包含了错误的模块,请告诉我。
* 替代方案
在写这篇文章时,我研究了
pip
和setuptools
的API。可能通过一个模块就能获取这些信息,但你真的需要熟悉那个API。在我开始之前,有人告诉我
six
有一个专门解决这个问题的函数。虽然这听起来合理,但我自己找不到。
sys.builtin_module_names
这是一个字符串的元组,里面列出了所有已经编译进这个Python解释器的模块名称。注意,这些信息是唯一的,其他方法无法获取,比如使用modules.keys()只能列出已经导入的模块。