列出作为 python 包一部分的所有模块?
Posted
技术标签:
【中文标题】列出作为 python 包一部分的所有模块?【英文标题】:List all the modules that are part of a python package? 【发布时间】:2010-12-15 00:42:04 【问题描述】:有没有一种直接的方法可以找到作为 python 包一部分的所有模块?我找到了this old discussion,这并不是真正的结论,但在我推出自己的基于 os.listdir() 的解决方案之前,我很想有一个明确的答案。
【问题讨论】:
@S.Lott:有更通用的解决方案可用,python 包并不总是在文件系统的目录中,但也可以在 zip 中。 为什么要重新发明***?如果 python 在 Python 4、pkgutil 中获取超模块并使用它进行更新,我的代码仍然可以工作。我喜欢使用可用的抽象。使用提供的显而易见的方法,它经过测试并且已知有效。重新实现.. 现在您必须自己找到并解决每个角落案例。 @S.Lott:所以每次应用程序启动时,如果安装在其中,它会解压缩自己的鸡蛋来检查这一点?请针对我的项目提交一个补丁,以在此功能中重新发明***:git.gnome.org/cgit/kupfer/tree/kupfer/plugins.py#n17。请考虑鸡蛋和普通目录,不要超过 20 行。 @S.Lott:为什么你不明白它是相关的,这是你无法理解的。以编程方式发现这一点是因为 应用程序 对包的内容感兴趣,而不是用户。 当然我的意思是编程!否则我不会提到“使用 os.listdir() 推出我自己的解决方案” 【参考方案1】:是的,您想要基于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
【讨论】:
pkgutil.iter_modules
返回的importer
是什么?我可以用它来导入模块而不是使用这个看似“hackish”的__import__(modname, fromlist="dummy")
吗?
我可以像这样使用导入器:m = importer.find_module(modname).load_module(modname)
然后m
是模块,例如:m.myfunc()
@chrisleague 我在 python 2.7 中使用了你的方法,但现在我需要继续使用 python 3.4,所以你知道在 python 3 pkutil.iter_modules 产生 (module_finder, name, ispkg) 而不是(模块加载器,名称,ispkg)。我该怎么做才能让它像以前一样工作?
您的第一个示例产生以下错误:"AttributeError: 'module' object has no attribute '_path_'" 与此有什么关系Python版本? (我使用 Python 2.7)
@Apostolos,您在路径的任一侧只使用了一个下划线(即_path_
)。两边应该有两个,总共四个(即__path__
)。【参考方案2】:
适合这项工作的工具是 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 方法,它递归地遍历所有 可用的模块。
【讨论】:
walk_packages
现在在文档中:docs.python.org/library/pkgutil.html#pkgutil.walk_packages
您的第二个示例产生以下错误:"AttributeError: 'module' object has no attribute '_path_'" - 我没有测试它与“scipy”一起使用,但与其他一些软件包一起使用。这与Python版本有什么关系吗? (我使用 Python 2.7)
@Apostolos:path
前后应该有两个下划线(_
)——即use package.__path__
,而不是package._path_
。尝试剪切和粘贴代码可能比重新键入更容易。
我写评论的时候有两个! :) 但它们已被系统剥离。我的错;我应该放三个下划线。但是,如果我想使用斜体就可以了,但我没有! ......这是一个损失损失的情况。 :) 无论如何,当我运行代码时,我当然使用了其中的两个。 (我复制粘贴了代码。)
@Apostolos:确保变量package
指向一个包,而不是一个模块。模块是文件,而包是目录。 All packages have the __path__
attribute(...除非有人出于某种原因删除了该属性。)【参考方案3】:
这对我有用:
import types
for key, obj in nltk.__dict__.iteritems():
if type(obj) is types.ModuleType:
print key
【讨论】:
这在两种情况下会失败 1. 包并不总是将其子模块显式导入***命名空间 2. 包可能会将其他 3rd-party 模块导入其***命名空间【参考方案4】:我正在寻找一种方法来重新加载我在包中实时编辑的所有子模块。它是上述答案/cmets 的组合,因此我决定将其作为答案而不是评论发布在这里。
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))
【讨论】:
【参考方案5】:这是我想不到的一种方法:
>>> 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
文件中,这也会找到可能不一定位于包的子目录中的模块,所以这取决于你的意思包的“一部分”。
【讨论】:
对不起,这没有用。除了误报,它也只会找到已经导入的包子模块。以上是关于列出作为 python 包一部分的所有模块?的主要内容,如果未能解决你的问题,请参考以下文章