Перечислить все модули, входящие в пакет python?

107

Есть ли простой способ найти все модули, входящие в пакет python? Я нашел это старое обсуждение , которое не является окончательным, но мне хотелось бы получить определенный ответ, прежде чем я разверну свое собственное решение на основе os.listdir ().

static_rtti
источник
6
@ S.Lott: Существуют более общие решения, пакеты python не всегда находятся в каталогах файловой системы, но также могут быть внутри zip-архивов.
u0b34a0f6ae
4
зачем изобретать велосипед? Если python получит гипермодули в Python 4, pkgutil и обновит их, мой код все равно будет работать. Мне нравится использовать доступные абстракции. Используйте предоставленный очевидный метод, он проверен и работает. Реализуя это заново ... теперь вы должны сами находить и обходить каждый уголок.
u0b34a0f6ae
1
@ S.Lott: То есть каждый раз, когда приложение запускается, оно будет распаковывать свое собственное яйцо, если оно установлено внутри одного, просто чтобы проверить это? Отправьте патч для моего проекта, чтобы изобрести колесо в этой функции: git.gnome.org/cgit/kupfer/tree/kupfer/plugins.py#n17 . Пожалуйста, учитывайте и яйца, и обычные каталоги, не превышайте 20 строк.
u0b34a0f6ae
1
@ S.Lott: Почему вы не понимаете, что это актуально, вы не можете понять. Обнаружение этого программным способом означает, что приложение интересуется содержимым пакета, а не пользователем.
u0b34a0f6ae
3
Конечно, программно! В противном случае я бы не упомянул «развертывание собственного решения с помощью os.listdir ()»
static_rtti

Ответы:

145

Да, вам нужно что-то на основе pkgutilили похожее - таким образом вы можете обрабатывать все пакеты одинаково, независимо от того, находятся ли они в яйцах или zip-файлах (где 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
u0b34a0f6ae
источник
9
что это importerвозвращает pkgutil.iter_modules? Могу ли я использовать его для импорта модуля вместо того, чтобы использовать этот кажущийся «хакерский» __import__(modname, fromlist="dummy")?
MestreLion 05
29
Я смог использовать импортер следующим образом: m = importer.find_module(modname).load_module(modname)а затем mмодуль, например:m.myfunc()
chrisleague
@chrisleague Я использовал метод ur с python 2.7, но теперь мне нужно перейти на python 3.4, так что вы знаете, что в python 3 pkutil.iter_modules дает (module_finder, name, ispkg) вместо (module_loader, name, ispkg). Что я могу сделать, чтобы он работал как предыдущий?
Crax
В вашем первом примере возникает следующая ошибка: «AttributeError: объект 'module' не имеет атрибута ' _path_ Имеет ли это какое-либо отношение к версии Python? (Я использую Python 2.7)
Апостолос
@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, который рекурсивно просматривает все доступные модули.

Unutbu
источник
5
walk_packagesтеперь в документации: docs.python.org/library/pkgutil.html#pkgutil.walk_packages
Механическая улитка
1
Во втором примере возникает следующая ошибка: «AttributeError: объект 'module' не имеет атрибута ' _path_ - я тестировал его не с помощью 'scipy', а с несколькими другими пакетами. Имеет ли это какое-либо отношение к версии Python? (Я использую Python 2.7)
Апостолос
1
@Apostolos: _до и после должно быть два символа подчеркивания ( ), pathто есть использоватьpackage.__path__ вместо package._path_. Возможно, будет проще попробовать вырезать и вставить код, чем набирать его заново.
unutbu
На момент написания комментария их было двое! :) Но их система раздела. Виноват; Я должен был поставить три подчеркивания. Но тогда это было бы нормально, если бы я хотел использовать курсив, чего я не делал! ... Это ситуация потери-потери. :) Во всяком случае, когда я запускал код, я, конечно, использовал два из них. (Я скопировал код.)
Апостолос
@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))
user1767754
источник
-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