列出所有属于python软件包的模块吗?


107

有没有一种直接的方法来查找python软件包中的所有模块?我已经找到了这个旧的讨论,这并不是真正的结论,但是我很想在我基于os.listdir()推出自己的解决方案之前有一个明确的答案。


6
@ S.Lott:有更多可用的常规解决方案,python软件包并不总是位于文件系统的目录中,但也可以位于zip内部。
u0b34a0f6ae

4
为什么要重新发明轮子?如果python在Python 4中获取了超级模块pkgutil并进行了更新,则我的代码仍然有效。我喜欢使用可用的抽象。使用提供的显而易见的方法,它经过测试并已知可以工作。重新实现该功能..现在您必须自己找到并处理每个角落的情况。
u0b34a0f6ae 2009年

1
@ S.Lott:因此,每当应用程序启动时,如果将其安装在一个鸡蛋中只是为了进行检查,它将解压缩它自己的鸡蛋?请针对我的项目提交补丁,以重新使用此功能:git.gnome.org/cgit/kupfer/tree/kupfer/plugins.py#n17。请同时考虑鸡蛋和普通目录,不要超过20行。
u0b34a0f6ae

1
@ S.Lott:为什么您不了解它是相关的,这是您无法理解的。以编程方式发现此内容与应用程序对软件包内容(而不是用户)感兴趣有关。
u0b34a0f6ae

3
我当然是指以编程方式!否则,我不会提到“使用os.listdir()推出自己的解决方案”
static_rtti

Answers:


145

是的,您需要某种基于pkgutil或相似的东西-这样,您可以将所有软件包都视为相同,而不管它们是放在鸡蛋还是拉链中(在os.listdir都不起作用的地方)。

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)

如何导入它们呢?您可以__import__照常使用:

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
prefix = package.__name__ + "."
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, prefix):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)
    module = __import__(modname, fromlist="dummy")
    print "Imported", module

9
这是importerpkgutil.iter_modules什么返回的?我可以使用它来导入模块,而不是使用这个看似“ hackish”的模块__import__(modname, fromlist="dummy")吗?
MestreLion 2013年

29
我可以这样使用导入程序:m = importer.find_module(modname).load_module(modname)然后m是模块,例如:m.myfunc()
chrisleague

@chrisleague我在python 2.7中使用了ur方法,但是现在我需要在python 3.4上继续使用,因此您知道在python 3中pkutil.iter_modules会产生(module_finder,name,ispkg)而不是(module_loader,name,ispkg)。如何使它像上一个一样工作?
crax

您的第一个示例将产生以下错误:“ AttributeError:'模块'对象没有属性' _path_ '”这与Python版本有关吗?(我使用Python 2.7)
Apostolos

@Apostolos,您在路径的任一侧(即_path_)仅使用一个下划线。两侧应有两个,总共四个(即__path__)。
therealmitchconnors

46

这项工作的正确工具是pkgutil.walk_packages。

要列出系统上的所有模块:

import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=None, onerror=lambda x: None):
    print(modname)

请注意,walk_packages会导入所有子包,但不会导入子模块。

如果您希望列出某个程序包的所有子模块,则可以使用如下代码:

import pkgutil
import scipy
package=scipy
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__,
                                                      prefix=package.__name__+'.',
                                                      onerror=lambda x: None):
    print(modname)

iter_modules仅列出一级深度的模块。walk_packages获取所有子模块。例如,对于scipy,walk_packages返回

scipy.stats.stats

而iter_modules仅返回

scipy.stats

pkgutil的文档(http://docs.python.org/library/pkgutil.html)没有列出/usr/lib/python2.6/pkgutil.py中定义的所有有趣功能。

也许这意味着功能不是“公共”界面的一部分,并且可能会发生变化。

但是,至少从Python 2.6起(也许是早期版本?),pkgutil带有walk_packages方法,该方法递归地遍历所有可用模块。



1
您的第二个示例产生以下错误:“ AttributeError:'模块'对象没有属性' _path_ '” -我没有使用'scipy'对其进行测试,而是使用了一些其他软件包。这与Python版本有关吗?(我使用Python 2.7)
Apostolos

1
@Apostolos:_前后应有两个下划线()path,即使用package.__path__而不是package._path_。尝试剪切和粘贴代码比重新键入代码更容易。
unutbu

当我写评论时,有两个人!:)但是它们已被系统删除。我的错; 我应该强调三个重点。但是,如果我要使用斜体,那是可以的,但我没有!...这是一种亏损-亏损的情况。:)无论如何,当我运行代码时,我当然使用了其中两个。(我复制粘贴了代码。)
Apostolos

@Apostolos:确保变量package指向软件包,而不是模块。模块是文件,而包是目录。所有软件包都具有__path__属性(...,除非有人出于某种原因删除了该属性。)
unutbu

2

这对我有用:

import types

for key, obj in nltk.__dict__.iteritems():
    if type(obj) is types.ModuleType: 
        print key

1
这种失败在两个方面1包不总是明确地导入自己的子模块到顶级命名空间2.包可以导入其他第三方模块集成到他们的顶级命名空间
WIM

0

我一直在寻找一种方法来重新加载我正在编辑的程序包中的所有子模块。它是上述答案/评论的组合,因此我决定将其发布在此处,作为答案而不是评论。

package=yourPackageName
import importlib
import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__+'.', onerror=lambda x: None):
    try:
        modulesource = importlib.import_module(modname)
        reload(modulesource)
        print("reloaded: {}".format(modname))
    except Exception as e:
        print('Could not load {} {}'.format(modname, e))

-4

这是我的头上的一种方法:

>>> import os
>>> filter(lambda i: type(i) == type(os), [getattr(os, j) for j in dir(os)])
[<module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'errno' (built-in)>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'sys' (built-in)>]

它肯定可以清理和改进。

编辑:这是一个稍微更好的版本:

>>> [m[1] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
[<module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'errno' (built-in)>, <module 'sys' (built-in)>]
>>> [m[0] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
['_copy_reg', 'UserDict', 'path', 'errno', 'sys']

注意:如果将模块拉入__init__.py文件中,它们也将找到不一定位于包子目录中的模块,因此取决于您“包的一部分”的含义。


对不起,那没有用。除了误报,它也只会找到包中已经导入的子模块。
u0b34a0f6ae 2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.