Перерахуйте всі модулі, що входять до пакету 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чи подібного - таким чином ви можете обробляти всі пакунки однаково, незалежно від того, чи є вони в яйцях чи блискавках чи так (де 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

29
Мені вдалося скористатись імпортером так: m = importer.find_module(modname).load_module(modname)а далі m- модуль, наприклад:m.myfunc()
chrisleague

@chrisleague Я використовував метод ур з python 2.7, але тепер мені потрібно перейти до python 3.4, так що ви знаєте, що в python 3 pkutil.iter_modules виходить (module_finder, ім'я, ispkg) замість (module_loader, ім'я, 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, який рекурсивно проходить через всі наявні модулі.


5
walk_packagesтепер знаходиться в документації: docs.python.org/library/pkgutil.html#pkgutil.walk_packages
Механічний равлик

1
У вашому другому прикладі виникає така помилка: "AttributeError:" модуль "об'єкта не має атрибута" _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)>]

Це, безумовно, можна було почистити та покращити.

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файл, тому це залежить від того, що ви маєте на увазі під "частиною" пакета.


вибачте, що не має користі. Крім помилкових позитивів, він також знайде лише імпортні підмодулі пакунків.
u0b34a0f6ae
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.