确定给定的Python模块是否为内置模块

22 投票
4 回答
10168 浏览
提问于 2025-04-16 11:23

我正在处理一些模块的解析和检查,但我不想解析内置模块。现在,内置模块没有像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 个回答

4

你可以使用 imp.is_builtin 来检查一个模块名是否是内置模块,但我想不出有什么可靠的方法可以深入查看一个模块对象。

你也可以尝试以下方法:

>>> import imp
>>> f, path, desc = imp.find_module("sys")
>>> desc
('', '', 6)
>>> desc[2] == imp.C_BUILTIN
True
11

如果你只是简单地考虑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_distributionssite。但pip返回的模块名称是它们在PyPi上的样子,而不是在源文件中导入时的样子。有些特殊的包会漏掉,比如:
      • requests-futures在导入时是requests_futures
      • colors,在PyPi上其实是ansicolors,这让任何合理的判断都变得复杂。
    • 我相信有些使用频率低的模块没有在它们的包中包含top_level.txt。但这覆盖了我所有的使用场景,似乎在所有正确配置的情况下都能工作。
  • system_modules

    • 如果你不明确请求这些模块,你是得不到系统模块的,比如sysgcerrno和其他一些可选模块
  • top_level_libs

    • 调用distutils.sysconfig.get_python_lib(standard_lib=True)会返回平台无关的标准库的顶层目录。
    • 这些模块容易被忽视,因为它们可能不在与其他模块相同的Python路径下。如果你在OSX上并运行虚拟环境,这些模块实际上会从系统安装中导入。这些模块包括emailloggingxml等。

结论

在我的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

我在这里放了一个代码和输出的链接。如果你觉得我漏掉了某个类或者包含了错误的模块,请告诉我。

* 替代方案

  • 在写这篇文章时,我研究了pipsetuptools的API。可能通过一个模块就能获取这些信息,但你真的需要熟悉那个API。

  • 在我开始之前,有人告诉我six有一个专门解决这个问题的函数。虽然这听起来合理,但我自己找不到。

13

sys.builtin_module_names

这是一个字符串的元组,里面列出了所有已经编译进这个Python解释器的模块名称。注意,这些信息是唯一的,其他方法无法获取,比如使用modules.keys()只能列出已经导入的模块。

撰写回答