Alle Module auflisten, die Teil eines Python-Pakets sind?

107

Gibt es eine einfache Möglichkeit, alle Module zu finden, die Teil eines Python-Pakets sind? Ich habe diese alte Diskussion gefunden , die nicht wirklich schlüssig ist, aber ich hätte gerne eine eindeutige Antwort, bevor ich meine eigene Lösung basierend auf os.listdir () herausbringe.

static_rtti
quelle
6
@ S.Lott: Es gibt allgemeinere Lösungen, Python-Pakete befinden sich nicht immer in Verzeichnissen im Dateisystem, sondern können sich auch in Zips befinden.
u0b34a0f6ae
4
Warum das Rad neu erfinden? Wenn Python in Python 4, pkgutil Hypermodule erwirbt und damit aktualisiert, funktioniert mein Code weiterhin. Ich verwende gerne verfügbare Abstraktionen. Verwenden Sie die offensichtliche Methode, die getestet wurde und bekanntermaßen funktioniert. Neu implementieren ... jetzt müssen Sie jeden Eckfall selbst finden und bearbeiten.
u0b34a0f6ae
1
@ S.Lott: Also wird die Anwendung jedes Mal, wenn sie gestartet wird, ihr eigenes Ei entpacken, wenn sie in einem installiert ist, nur um dies zu überprüfen? Bitte senden Sie einen Patch gegen mein Projekt, um das Rad in dieser Funktion neu zu erfinden: git.gnome.org/cgit/kupfer/tree/kupfer/plugins.py#n17 . Bitte beachten Sie sowohl Eier als auch normale Verzeichnisse, nicht mehr als 20 Zeilen.
u0b34a0f6ae
1
@ S.Lott: Warum du nicht verstehst, dass es relevant ist, kannst du nicht verstehen. Um dies programmgesteuert zu erkennen, interessiert sich die Anwendung für den Inhalt eines Pakets, nicht für den Benutzer.
u0b34a0f6ae
3
Natürlich meine ich programmatisch! Andernfalls hätte ich nicht erwähnt, dass "meine eigene Lösung mit os.listdir () eingeführt wird"
static_rtti

Antworten:

145

Ja, Sie möchten etwas basierend auf pkgutiloder ähnlichem - auf diese Weise können Sie alle Pakete gleich behandeln, unabhängig davon, ob sie sich in Eiern oder Reißverschlüssen befinden oder nicht (wobei os.listdir nicht hilft).

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)

Wie importiere ich sie auch? Sie können einfach __import__wie gewohnt verwenden:

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
quelle
9
Wovon wird das importerzurückgegeben pkgutil.iter_modules? Kann ich damit ein Modul importieren, anstatt dieses scheinbar "hackisch" zu verwenden __import__(modname, fromlist="dummy")?
MestreLion
29
Ich konnte den Importer so verwenden: m = importer.find_module(modname).load_module(modname)und dann mist das Modul, also zum Beispiel:m.myfunc()
chrisleague
@chrisleague Ich habe unsere Methode mit Python 2.7 verwendet, aber jetzt muss ich mit Python 3.4 fortfahren, damit Sie wissen, dass in Python 3 pkutil.iter_modules anstelle von (module_loader, name, ispkg) (module_finder, name, ispkg) ergibt. Was kann ich tun, damit es wie das vorherige funktioniert?
Crax
Ihr erstes Beispiel erzeugt den folgenden Fehler: "AttributeError: ' module' - Objekt hat kein Attribut' _path_ '" Hat dies etwas mit der Python-Version zu tun? (Ich benutze Python 2.7)
Apostolos
@Apostolos, Sie verwenden nur einen Unterstrich auf beiden Seiten des Pfades (dh _path_). Auf jeder Seite sollten sich zwei befinden, also insgesamt vier (dh __path__).
Therealmitchconnors
46

Das richtige Werkzeug für diesen Job ist pkgutil.walk_packages.

So listen Sie alle Module auf Ihrem System auf:

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

Beachten Sie, dass walk_packages alle Unterpakete importiert, jedoch keine Submodule.

Wenn Sie alle Submodule eines bestimmten Pakets auflisten möchten, können Sie Folgendes verwenden:

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 listet nur die Module auf, die eine Ebene tief sind. walk_packages ruft alle Submodule ab. Im Fall von scipy wird beispielsweise walk_packages zurückgegeben

scipy.stats.stats

während iter_modules nur zurückgibt

scipy.stats

In der Dokumentation zu pkgutil ( http://docs.python.org/library/pkgutil.html ) sind nicht alle in /usr/lib/python2.6/pkgutil.py definierten interessanten Funktionen aufgeführt.

Vielleicht bedeutet dies, dass die Funktionen nicht Teil der "öffentlichen" Schnittstelle sind und Änderungen unterliegen.

Zumindest ab Python 2.6 (und möglicherweise früheren Versionen?) Liefert pkgutil jedoch eine walk_packages-Methode, die alle verfügbaren Module rekursiv durchläuft.

unutbu
quelle
5
walk_packagesist jetzt in der Dokumentation: docs.python.org/library/pkgutil.html#pkgutil.walk_packages
Mechanische Schnecke
1
Ihr zweites Beispiel erzeugt den folgenden Fehler: "AttributeError: ' module' -Objekt hat kein Attribut' _path_ '" - Ich habe es nicht mit' scipy 'getestet, sondern mit einigen anderen Paketen. Hat das etwas mit der Python-Version zu tun? (Ich benutze Python 2.7)
Apostolos
1
@Apostolos: Es sollte zwei Unterstrichen (sein _) vor und nach path- das ist verwendenpackage.__path__ statt package._path_. Es ist möglicherweise einfacher, den Code auszuschneiden und einzufügen, als ihn erneut einzugeben.
Unutbu
Es gab zwei von ihnen, als ich den Kommentar schrieb! :) Aber sie wurden vom System entfernt. Mein Fehler; Ich hätte drei Unterstriche setzen sollen. Aber dann wäre das in Ordnung, wenn ich Kursivschrift verwenden wollte, was ich nicht tat! ... Es ist eine Verlust-Verlust-Situation. :) Wie auch immer, als ich den Code ausführte, habe ich natürlich zwei davon verwendet. (Ich habe den Code kopiert.)
Apostolos
@Apostolos: Stellen Sie sicher, dass die Variable packageauf ein Paket zeigt, nicht auf ein Modul. Module sind Dateien, während Pakete Verzeichnisse sind. Alle Pakete haben das __path__Attribut (... es sei denn, jemand hat das Attribut aus irgendeinem Grund gelöscht.)
unutbu
2

Das funktioniert bei mir:

import types

for key, obj in nltk.__dict__.iteritems():
    if type(obj) is types.ModuleType: 
        print key
DarinP
quelle
1
Dies schlägt auf zwei Arten fehl: 1. Pakete importieren ihre Submodule nicht immer explizit in den Namespace der obersten Ebene. 2. Pakete importieren möglicherweise andere Module von Drittanbietern in ihren Namespace der obersten Ebene
wim
0

Ich suchte nach einer Möglichkeit, alle Submodule, die ich bearbeite, live in meinem Paket neu zu laden. Es ist eine Kombination der obigen Antworten / Kommentare, daher habe ich beschlossen, sie hier als Antwort und nicht als Kommentar zu veröffentlichen.

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
quelle
-4

Hier ist eine Möglichkeit, die mir aus dem Kopf geht:

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

Es könnte sicherlich aufgeräumt und verbessert werden.

EDIT: Hier ist eine etwas schönere Version:

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

HINWEIS: Hier werden auch Module gefunden, die sich möglicherweise nicht unbedingt in einem Unterverzeichnis des Pakets befinden, wenn sie in die __init__.pyDatei des Pakets gezogen werden. Dies hängt also davon ab, was Sie unter "Teil" eines Pakets verstehen.

Steve Losh
quelle
Entschuldigung, das hat keinen Sinn. Abgesehen von den Fehlalarmen werden auch nur bereits importierte Submodule von Paketen gefunden.
u0b34a0f6ae