Wymień wszystkie moduły, które są częścią pakietu Pythona?

107

Czy istnieje prosty sposób na znalezienie wszystkich modułów, które są częścią pakietu Pythona? Znalazłem tę starą dyskusję , która nie jest do końca rozstrzygająca, ale chciałbym uzyskać ostateczną odpowiedź, zanim wdrożę własne rozwiązanie oparte na os.listdir ().

static_rtti
źródło
6
@ S.Lott: Dostępne są bardziej ogólne rozwiązania, pakiety Pythona nie zawsze znajdują się w katalogach w systemie plików, ale mogą również znajdować się wewnątrz zipów.
u0b34a0f6ae
4
po co wymyślać koło na nowo? Jeśli Python pozyska hipermoduły w Pythonie 4, pkgutil i zaktualizuje się o to, mój kod będzie nadal działał. Lubię korzystać z dostępnych abstrakcji. Użyj oczywistej metody, która została przetestowana i działa. Reimplementując to… teraz musisz sam znaleźć i obejść każdą narożną sprawę.
u0b34a0f6ae
1
@ S.Lott: Więc za każdym razem, gdy aplikacja się uruchamia, rozpakuje swoje własne jajko, jeśli jest zainstalowane w jednym, tylko po to, aby to sprawdzić? Prześlij poprawkę do mojego projektu, aby ponownie wymyślić koło w tej funkcji: git.gnome.org/cgit/kupfer/tree/kupfer/plugins.py#n17 . Proszę wziąć pod uwagę zarówno jajka, jak i zwykłe katalogi, nie przekraczaj 20 linii.
u0b34a0f6ae
1
@ S.Lott: Dlaczego nie rozumiesz, że jest to istotne, nie możesz zrozumieć. Odkrycie tego programowe polega na tym, że aplikacja interesuje się zawartością pakietu, a nie użytkownikiem.
u0b34a0f6ae
3
Oczywiście mam na myśli programowo! W przeciwnym razie nie wspomniałbym o „wprowadzeniu własnego rozwiązania z os.listdir ()”
static_rtti

Odpowiedzi:

145

Tak, chcesz czegoś opartego pkgutillub podobnego - w ten sposób możesz traktować wszystkie pakiety jednakowo, niezależnie od tego, czy są w jajkach, czy w suwakach (gdzie os.listdir nie pomoże).

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)

Jak je też zaimportować? Możesz po prostu użyć __import__normalnie:

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
źródło
9
przez co to jest importerzwracane pkgutil.iter_modules? Czy mogę go użyć do zaimportowania modułu zamiast używać tego pozornie „hakerskiego” __import__(modname, fromlist="dummy")?
MestreLion,
29
Udało mi się skorzystać z importera w ten sposób: m = importer.find_module(modname).load_module(modname)a potem mjest moduł, a więc na przykład:m.myfunc()
chrisleague
@chrisleague Używałem metody ur z pythonem 2.7, ale teraz muszę przejść do pythona 3.4, więc wiesz, że w pythonie 3 pkutil.iter_modules daje (module_finder, name, ispkg) zamiast (module_loader, name, ispkg). Co mogę zrobić, aby działał jak poprzedni?
crax
Twój pierwszy przykład daje następujący błąd: „AttributeError: obiekt 'module' nie ma atrybutu ' _path_ '” Czy ma to coś wspólnego z wersją Pythona? (Używam Pythona 2.7)
Apostolos
@Apostolos, używasz tylko jednego podkreślenia po obu stronach ścieżki (tj _path_.). Powinny być dwa po obu stronach, w sumie cztery (tj __path__.).
therealmitchconnors
46

Odpowiednim narzędziem do tego zadania jest pkgutil.walk_packages.

Aby wyświetlić wszystkie moduły w systemie:

import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=None, onerror=lambda x: None):
    print(modname)

Należy pamiętać, że walk_packages importuje wszystkie podpakiety, ale nie moduły podrzędne.

Jeśli chcesz wyświetlić wszystkie podmoduły określonego pakietu, możesz użyć czegoś takiego:

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 wyświetla tylko te moduły, które są głębokie na jeden poziom. walk_packages pobiera wszystkie moduły podrzędne. Na przykład w przypadku scipy zwraca walk_packages

scipy.stats.stats

podczas gdy iter_modules tylko zwraca

scipy.stats

Dokumentacja pkgutil ( http://docs.python.org/library/pkgutil.html ) nie zawiera wszystkich interesujących funkcji zdefiniowanych w /usr/lib/python2.6/pkgutil.py.

Być może oznacza to, że funkcje nie są częścią „publicznego” interfejsu i mogą ulec zmianie.

Jednak przynajmniej od Pythona 2.6 (i być może wcześniejszych wersji?) Pkgutil zawiera metodę walk_packages, która rekurencyjnie przechodzi przez wszystkie dostępne moduły.

unutbu
źródło
5
walk_packagesjest teraz w dokumentacji: docs.python.org/library/pkgutil.html#pkgutil.walk_packages
Ślimak mechaniczny
1
Twój drugi przykład daje następujący błąd: „AttributeError: obiekt 'module' nie ma atrybutu ' _path_ '” - nie testowałem go z 'scipy', ale z kilkoma innymi pakietami. Czy ma to coś wspólnego z wersją Pythona? (Używam Pythona 2.7)
Apostolos
1
@Apostolos: Powinny być dwa podkreślenia ( _) przed i po path- to znaczy użyjpackage.__path__ zamiast package._path_. Może być łatwiej spróbować wyciąć i wkleić kod, zamiast wpisywać go ponownie.
unutbu
Było ich dwóch, kiedy pisałem komentarz! :) Ale zostały one usunięte przez system. Mój błąd; Powinienem był umieścić trzy podkłady. Ale wtedy byłoby OK, gdybym chciał użyć kursywy, czego nie zrobiłem! ... To sytuacja ze stratą. :) Zresztą, uruchamiając kod użyłem oczywiście dwóch z nich. (Skopiowałem i wkleiłem kod.)
Apostolos
@Apostolos: Upewnij się, że zmienna packagewskazuje na pakiet, a nie moduł. Moduły to pliki, a pakiety to katalogi. Wszystkie pakiety mają __path__atrybut (... chyba że ktoś usunął atrybut z jakiegoś powodu.)
unutbu
2

To działa dla mnie:

import types

for key, obj in nltk.__dict__.iteritems():
    if type(obj) is types.ModuleType: 
        print key
DarinP
źródło
1
To kończy się niepowodzeniem na dwa sposoby 1. Pakiety nie zawsze jawnie importują swoje moduły podrzędne do przestrzeni nazw najwyższego poziomu 2. Pakiety mogą importować inne moduły firm trzecich do ich przestrzeni nazw najwyższego poziomu
wim
0

Szukałem sposobu na przeładowanie wszystkich podmodułów, które edytuję na żywo w moim pakiecie. Jest to połączenie powyższych odpowiedzi / komentarzy, więc zdecydowałem się zamieścić to tutaj jako odpowiedź, a nie komentarz.

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
źródło
-4

Oto jeden sposób, z góry mojej głowy:

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

Z pewnością można to oczyścić i ulepszyć.

EDYCJA: Oto nieco ładniejsza wersja:

>>> [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']

UWAGA: Spowoduje to również znalezienie modułów, które niekoniecznie muszą znajdować się w podkatalogu pakietu, jeśli są wciągnięte w jego __init__.pyplik, więc zależy to od tego, co rozumiesz przez „część” pakietu.

Steve Losh
źródło
przepraszam, to nie ma sensu. Oprócz fałszywych alarmów, znajdzie tylko już zaimportowane podmoduły pakietów.
u0b34a0f6ae