Come posso ottenere un elenco di tutte le classi all'interno del modulo corrente in Python?


301

Ho visto molti esempi di persone che estraevano tutte le classi da un modulo, di solito qualcosa del tipo:

# foo.py
class Foo:
    pass

# test.py
import inspect
import foo

for name, obj in inspect.getmembers(foo):
    if inspect.isclass(obj):
        print obj

Eccezionale.

Ma non riesco a scoprire come ottenere tutte le classi dal modulo corrente .

# foo.py
import inspect

class Foo:
    pass

def print_classes():
    for name, obj in inspect.getmembers(???): # what do I do here?
        if inspect.isclass(obj):
            print obj

# test.py
import foo

foo.print_classes()

Questo è probabilmente qualcosa di veramente ovvio, ma non sono stato in grado di trovare nulla. Qualcuno può darmi una mano?


2
C'era un PEP per una funzionalità come questa, ma è stata respinta.
Gary van der Merwe,

Cosa c'è che non va nella lettura della fonte "class"? Perché non funziona?
S. Lott,

67
Immagino che la domanda riguardi il voler automatizzare alcune attività, quindi è importante che sia fatto a livello di programmazione. Presumibilmente l'interrogante pensa che farlo manualmente, leggendo il codice sorgente con gli occhi, potrebbe essere ripetitivo, soggetto a errori o che richiede tempo.
Jonathan Hartley,

Risposte:


386

Prova questo:

import sys
current_module = sys.modules[__name__]

Nel tuo contesto:

import sys, inspect
def print_classes():
    for name, obj in inspect.getmembers(sys.modules[__name__]):
        if inspect.isclass(obj):
            print(obj)

E ancora meglio:

clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass)

Perché inspect.getmembers()prende un predicato.


9
Se importare classi in questo modulo a livello di modulo (cioè from optparse import OptionParser) , quei moduli sono inclusi nell'elenco di stampa. Come potrei evitarlo?
Chris

5
@phasetwenty, invece di inspect.isclass puoi avere qualcosa del tipo:inspect.getmembers(sys.modules[__name__], lambda member: member.__module__ == __name__ and isnpect.isclass)
Nadia Alramli

1
ma dict(inspect.getmembers(sys.modules[__name__])) == globals()è sempre True, quindi perché le importazioni?
Kojiro,

16
La risposta di Nadia è quasi corretta. Meglio: inspect.getmembers(sys.modules[__name__], lambda member: inspect.isclass(member) and member.__module__ == __name__
William Budington il

1
@JohnM. perché Nadia ha dimenticato di chiamare isclass.
Alex Hall,

20

Che dire

g = globals().copy()
for name, obj in g.iteritems():

?


Questo è quello che faccio di solito. Le altre risposte sembrano molto più "pulite", però, non le conoscevano.
Mizipzor,

1
Mi sembra molto pulito, in particolare se si filtraisinstance(obj, types.ClassType)
kojiro

4
Mi piace meglio questa risposta perché funzionerà anche se il modulo corrente non è stato inserito in sys.modules, ad esempio da docs.python.org/2/library/functions.html#execfile
Chris Smith,

@ChrisSmith In particolare, ho scoperto oggi che alcuni debugger come pudbeseguono il tuo programma in questo modo, il che si traduce in una sys.modulesrottura casuale del codice durante il debug. globals()sembra un po 'brutto, ma sembra essere molto più affidabile.
Soren Bjornstad,

15

Non so se esiste un modo "corretto" per farlo, ma il tuo frammento è sulla strada giusta: aggiungi semplicemente import foofoo.py, do inspect.getmembers(foo)e dovrebbe funzionare bene.


Whoa, avrei pensato che questo avrebbe creato una dipendenza circolare o qualcosa del genere, ma funziona!
McCcclean,

Il motivo per cui non si ottiene una dipendenza circolare o un ciclo di importazione è che una volta importato un modulo, questo viene aggiunto allo spazio dei nomi globale. Quando il modulo importato viene eseguito e arriva a 'import foo', salta l'importazione perché il modulo è già disponibile a livello globale. Se esegui foo come main (come uno script) il modulo viene effettivamente eseguito due volte perché quando arrivi a 'import foo' main sarà nello spazio dei nomi globale ma non pippo. Dopo 'import foo' sia ' main ' che 'foo' saranno nello spazio dei nomi dei globi.
galinden,

11

Sono stato in grado di ottenere tutto ciò di cui avevo bisogno dal dirplus integrato getattr.

# Works on pretty much everything, but be mindful that 
# you get lists of strings back

print dir(myproject)
print dir(myproject.mymodule)
print dir(myproject.mymodule.myfile)
print dir(myproject.mymodule.myfile.myclass)

# But, the string names can be resolved with getattr, (as seen below)

Tuttavia, esce come una palla di capelli:

def list_supported_platforms():
    """
        List supported platforms (to match sys.platform)

        @Retirms:
            list str: platform names
    """
    return list(itertools.chain(
        *list(
            # Get the class's constant
            getattr(
                # Get the module's first class, which we wrote
                getattr(
                    # Get the module
                    getattr(platforms, item),
                    dir(
                        getattr(platforms, item)
                    )[0]
                ),
                'SYS_PLATFORMS'
            )
            # For each include in platforms/__init__.py 
            for item in dir(platforms)
            # Ignore magic, ourselves (index.py) and a base class.
            if not item.startswith('__') and item not in ['index', 'base']
        )
    ))

6
import pyclbr
print(pyclbr.readmodule(__name__).keys())

Si noti che il modulo browser di classe Python dello stdlib utilizza un'analisi di origine statica, quindi funziona solo per i moduli supportati da un .pyfile reale .


4

Se vuoi avere tutte le classi, che appartengono al modulo corrente, puoi usare questo:

import sys, inspect
def print_classes():
    is_class_member = lambda member: inspect.isclass(member) and member.__module__ == __name__
    clsmembers = inspect.getmembers(sys.modules[__name__], is_class_member)

Se usi la risposta di Nadia e stai importando altre classi sul tuo modulo, anche quelle verranno importate.

Ecco perché member.__module__ == __name__viene aggiunto al predicato utilizzato suis_class_member . Questa affermazione verifica che la classe appartenga davvero al modulo.

Un predicato è una funzione (richiamabile), che restituisce un valore booleano.


3

Un'altra soluzione che funziona in Python 2 e 3:

#foo.py
import sys

class Foo(object):
    pass

def print_classes():
    current_module = sys.modules[__name__]
    for key in dir(current_module):
        if isinstance( getattr(current_module, key), type ):
            print(key)

# test.py
import foo
foo.print_classes()

Questo non funziona in 3.6.8. Non ricevo alcun errore del modulo.
Aviral Srivastava,

3

Questa è la linea che uso per ottenere tutte le classi che sono state definite nel modulo corrente (cioè non importate). È un po 'lungo secondo PEP-8 ma puoi cambiarlo come ritieni opportuno.

import sys
import inspect

classes = [name for name, obj in inspect.getmembers(sys.modules[__name__], inspect.isclass) 
          if obj.__module__ is __name__]

Questo ti dà un elenco dei nomi delle classi. Se vuoi gli oggetti di classe stessi, tieni invece obj.

classes = [obj for name, obj in inspect.getmembers(sys.modules[__name__], inspect.isclass)
          if obj.__module__ is __name__]

Questo è stato più utile nella mia esperienza.



0

Penso che tu possa fare qualcosa del genere.

class custom(object):
    __custom__ = True
class Alpha(custom):
    something = 3
def GetClasses():
    return [x for x in globals() if hasattr(globals()[str(x)], '__custom__')]
print(GetClasses())`

se hai bisogno di lezioni personali


0

Mi trovo spesso a scrivere utilità da riga di comando in cui il primo argomento è destinato a fare riferimento a una di molte classi diverse. Ad esempio ./something.py feature command —-arguments, dov'è Featureuna classe ecommand è un metodo su quella classe. Ecco una classe di base che lo rende facile.

Il presupposto è che questa classe di base risieda in una directory accanto a tutte le sue sottoclassi. È quindi possibile chiamare ArgBaseClass(foo = bar).load_subclasses()che restituirà un dizionario. Ad esempio, se la directory è simile alla seguente:

  • arg_base_class.py
  • feature.py

Supponendo feature.pystrumenti class Feature(ArgBaseClass), quindi la precedente invocazione di load_subclassestornerà { 'feature' : <Feature object> }. Lo stesso kwargs( foo = bar) verrà passato alla Featureclasse.

#!/usr/bin/env python3
import os, pkgutil, importlib, inspect

class ArgBaseClass():
    # Assign all keyword arguments as properties on self, and keep the kwargs for later.
    def __init__(self, **kwargs):
        self._kwargs = kwargs
        for (k, v) in kwargs.items():
            setattr(self, k, v)
        ms = inspect.getmembers(self, predicate=inspect.ismethod)
        self.methods = dict([(n, m) for (n, m) in ms if not n.startswith('_')])

    # Add the names of the methods to a parser object.
    def _parse_arguments(self, parser):
        parser.add_argument('method', choices=list(self.methods))
        return parser

    # Instantiate one of each of the subclasses of this class.
    def load_subclasses(self):
        module_dir = os.path.dirname(__file__)
        module_name = os.path.basename(os.path.normpath(module_dir))
        parent_class = self.__class__
        modules = {}
        # Load all the modules it the package:
        for (module_loader, name, ispkg) in pkgutil.iter_modules([module_dir]):
            modules[name] = importlib.import_module('.' + name, module_name)

        # Instantiate one of each class, passing the keyword arguments.
        ret = {}
        for cls in parent_class.__subclasses__():
            path = cls.__module__.split('.')
            ret[path[-1]] = cls(**self._kwargs)
        return ret
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.