Elenca tutti i moduli che fanno parte di un pacchetto python?


107

C'è un modo semplice per trovare tutti i moduli che fanno parte di un pacchetto Python? Ho trovato questa vecchia discussione , che non è davvero conclusiva, ma mi piacerebbe avere una risposta definitiva prima di lanciare la mia soluzione basata su os.listdir ().


6
@ S.Lott: Sono disponibili soluzioni più generali, i pacchetti python non sono sempre nelle directory del filesystem, ma possono anche essere all'interno di zip.
u0b34a0f6ae

4
perché reinventare la ruota? Se python acquisisce hypermodules in Python 4, pkgutil e viene aggiornato con quello, il mio codice funzionerà ancora. Mi piace usare le astrazioni disponibili. Usa il metodo ovvio fornito, è testato e noto per funzionare. Reimplementandolo .. ora devi trovare e lavorare dietro ogni caso da solo.
u0b34a0f6ae

1
@ S.Lott: Quindi ogni volta che l'applicazione si avvia, decomprimerà il proprio uovo se installato all'interno di uno solo per verificarlo? Si prega di inviare una patch al mio progetto per reinventare la ruota in questa funzione: git.gnome.org/cgit/kupfer/tree/kupfer/plugins.py#n17 . Si prega di considerare sia le uova che le directory normali, non superare le 20 righe.
u0b34a0f6ae

1
@ S.Lott: Perché non capisci che è rilevante è qualcosa che non puoi capire. Scoprirlo a livello di codice significa che l' applicazione si interessa al contenuto di un pacchetto, non l'utente.
u0b34a0f6ae

3
Ovviamente intendo programmaticamente! Altrimenti non avrei menzionato "il lancio della mia soluzione con os.listdir ()"
static_rtti

Risposte:


145

Sì, vuoi qualcosa basato su pkgutilo simile - in questo modo puoi trattare tutti i pacchetti allo stesso modo indipendentemente dal fatto che siano in uova o zip o giù di lì (dove os.listdir non aiuta).

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)

Come importarli anche loro? Puoi semplicemente usare __import__normalmente:

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
da cosa viene importerrestituito pkgutil.iter_modules? Posso usarlo per importare un modulo invece di usare questo apparentemente "hacker" __import__(modname, fromlist="dummy")?
MestreLion

29
Sono stato in grado di utilizzare l'importatore in questo modo: m = importer.find_module(modname).load_module(modname)e poi mè il modulo, quindi ad esempio:m.myfunc()
chrisleague

@chrisleague stavo usando il tuo metodo con python 2.7, ma ora devo andare avanti con python 3.4, quindi sai che in python 3 pkutil.iter_modules produce (module_finder, name, ispkg) invece di (module_loader, name, ispkg). Cosa posso fare per farlo funzionare come il precedente?
crax

Il tuo primo esempio produce il seguente errore: "AttributeError: l'oggetto 'module' non ha attributo ' _path_ '" Ha qualcosa a che fare con la versione di Python? (Io uso Python 2.7)
Apostolos

@Apostolos, stai usando solo un trattino basso su entrambi i lati del percorso (cioè _path_). Dovrebbero essercene due su entrambi i lati, per un totale di quattro (cioè __path__).
therealmitchconnors

46

Lo strumento giusto per questo lavoro è pkgutil.walk_packages.

Per elencare tutti i moduli sul tuo sistema:

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

Tieni presente che walk_packages importa tutti i sottopacchetti, ma non i sottomoduli.

Se desideri elencare tutti i sottomoduli di un determinato pacchetto, puoi usare qualcosa del genere:

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 elenca solo i moduli che hanno una profondità di un livello. walk_packages ottiene tutti i sottomoduli. Nel caso di scipy, ad esempio, walk_packages restituisce

scipy.stats.stats

mentre iter_modules restituisce solo

scipy.stats

La documentazione su pkgutil ( http://docs.python.org/library/pkgutil.html ) non elenca tutte le funzioni interessanti definite in /usr/lib/python2.6/pkgutil.py.

Forse questo significa che le funzioni non fanno parte dell'interfaccia "pubblica" e sono soggette a modifiche.

Tuttavia, almeno a partire da Python 2.6 (e forse le versioni precedenti?) Pkgutil viene fornito con un metodo walk_packages che passa in modo ricorsivo attraverso tutti i moduli disponibili.



1
Il tuo secondo esempio produce il seguente errore: "AttributeError: l'oggetto 'module' non ha attributo ' _path_ '" - Non l'ho testato con 'scipy' ma con pochi altri pacchetti. Ha qualcosa a che fare con la versione di Python? (Io uso Python 2.7)
Apostolos

1
@Apostolos: dovrebbero esserci due trattini bassi ( _) prima e dopo path, ovvero, usapackage.__path__ invece di package._path_. Potrebbe essere più semplice provare a tagliare e incollare il codice anziché riscriverlo.
unutbu

C'erano due di loro, quando ho scritto il commento! :) Ma sono stati rimossi dal sistema. Colpa mia; Avrei dovuto mettere tre undercores. Ma poi, questo andrebbe bene se volessi usare il corsivo, cosa che non ho fatto! ... È una situazione di perdita-perdita. :) Comunque, quando eseguo il codice ne ho usati due, ovviamente. (Ho copiato e incollato il codice.)
Apostolos

@Apostolos: assicurati che la variabile packagepunti a un pacchetto, non a un modulo. I moduli sono file mentre i pacchetti sono directory. Tutti i pacchetti hanno l' __path__attributo (... a meno che qualcuno non abbia cancellato l'attributo per qualche motivo.)
unutbu

2

Questo funziona per me:

import types

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

1
Questo fallisce in due modi: 1. i pacchetti non importano sempre esplicitamente i loro sottomoduli nello spazio dei nomi di primo livello 2. i pacchetti possono importare altri moduli di terze parti nel loro spazio dei nomi di primo livello
wim

0

Stavo cercando un modo per ricaricare tutti i sottomoduli che sto modificando dal vivo nel mio pacchetto. È una combinazione delle risposte / commenti sopra, quindi ho deciso di pubblicarlo qui come risposta piuttosto che come commento.

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

Ecco un modo, dalla parte superiore della mia testa:

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

Potrebbe certamente essere ripulito e migliorato.

EDIT: ecco una versione leggermente più carina:

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

NOTA: Questo troverà anche i moduli che potrebbero non essere necessariamente situati in una sottodirectory del pacchetto, se sono inseriti nel suo __init__.pyfile, quindi dipende da cosa si intende per "parte di" un pacchetto.


scusa, non serve. A parte i falsi positivi, troverà solo sottomoduli di pacchetti già importati.
u0b34a0f6ae
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.