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


107

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


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

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, который рекурсивно просматривает все доступные модули.



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))

-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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.