2009-11-10 7 views
70

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

+0

бонусный вопрос: как вы импортировать найденные модули красиво? –

+0

Что не так с чтением исходного каталога? Какая еще информация вам нужна? Что случилось с 'ls' (или' dir')? –

+6

@ S.Lott: Доступны более общие решения, пакеты python не всегда находятся в каталогах в файловой системе, но также могут быть внутри zips. – u0b34a0f6ae

ответ

100

Да, вы хотите что-то, основанное на 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 
+6

Что это за 'импортер', возвращенный' pkgutil.iter_modules'? Могу ли я использовать его для импорта модуля вместо использования этого, казалось бы, «хакерского» '__import __ (modname, fromlist =" dummy ")'? – MestreLion

+22

Я смог использовать импортер следующим образом: 'm = importer.find_module (modname) .load_module (modname)', а затем 'm' является модулем, например, например:' m.myfunc() ' – chrisleague

+0

@chrisleague I использовал метод ur с python 2.7, но теперь мне нужно перейти на python 3.4, так что вы знаете, что в python 3 pkutil.iter_modules дает (module_finder, name, ispkg) вместо (module_loader, name, ispkg). Что я могу сделать, чтобы он работал как предыдущий? –

-2

Вот один из способов, с верхней частью моей головы:

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

Это, безусловно, может быть очищено и улучшено.

EDIT: Вот немного лучше версия:

>>> [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, так что это зависит от того, что вы подразумеваете под «частью» пакета.

+0

извините, это бесполезно. Помимо ложных срабатываний, он найдет только уже импортированные подмодули пакетов. – u0b34a0f6ae

30

Правильный инструмент для этой работы является 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 который рекурсивно ходит через все доступных модулей.

+4

'walk_packages' теперь находится в документации: http://docs.python.org/library/pkgutil.html#pkgutil.walk_packages –

+0

Ваш второй пример вызывает следующую ошибку: **« AttributeError: ' модуль 'не имеет атрибута' __path __ '"** - Я не тестировал его с помощью scipy, но с несколькими другими пакетами. Это что-то связано с версией Python? (Я использую Python 2.7) – Apostolos

+0

@Apostolos: должно быть два символа подчеркивания ('_') до и после' path' - то есть [use 'package .__ path__'] (https://stackoverflow.com/q/ 2699287/190597), а не 'package._path_'. Возможно, было бы проще попробовать вырезать и вставлять код, а не повторно вводить его. – unutbu

3

Это работает для меня:

import types 

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