Jak mogę uzyskać listę wszystkich klas w ramach bieżącego modułu w Pythonie?


301

Widziałem wiele przykładów ludzi wydobywających wszystkie klasy z modułu, zwykle coś takiego:

# foo.py
class Foo:
    pass

# test.py
import inspect
import foo

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

Niesamowite.

Ale nie mogę się dowiedzieć, jak uzyskać wszystkie klasy z bieżącego modułu.

# 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()

To prawdopodobnie coś naprawdę oczywistego, ale nic nie znalazłem. Czy ktoś może mi pomóc?


2
Wystąpił PEP dla takiej funkcji, ale został on odrzucony.
— Gary van der Merwe

Co jest złego w czytaniu źródła "class"? Dlaczego to nie zadziała?
— S.Lott,

66
Domyślam się, że pytanie dotyczy automatyzacji jakiegoś zadania, dlatego ważne jest, aby było to zrobione programowo. Prawdopodobnie pytający uważa, że ​​robienie tego ręcznie, czytając kod źródłowy oczami, może być powtarzalne, podatne na błędy lub czasochłonne.
— Jonathan Hartley,

Odpowiedzi:


386

Spróbuj tego:

import sys
current_module = sys.modules[__name__]

W twoim kontekście:

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

A nawet lepiej:

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

Ponieważ inspect.getmembers()wymaga orzeczenia.


9
Jeśli zaimportuję klasy do tego modułu na poziomie modułu (tj. from optparse import OptionParser), Moduły te zostaną uwzględnione na liście drukowania. Jak mogę tego uniknąć?
— Chris

5
@phasetwenty, zamiast inspect.isclass możesz mieć coś takiego:inspect.getmembers(sys.modules[__name__], lambda member: member.__module__ == __name__ and isnpect.isclass)
— Nadia Alramli

1
ale dict(inspect.getmembers(sys.modules[__name__])) == globals()jest zawsze True, więc dlaczego import?
— kojiro

16
Odpowiedź Nadii jest prawie poprawna. Lepiej: inspect.getmembers(sys.modules[__name__], lambda member: inspect.isclass(member) and member.__module__ == __name__
— William Budington,

1
@JohnM. ponieważ Nadia zapomniała zadzwonić isclass.
— Alex Hall

20

Co powiesz na

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

?


Tak zwykle robię. Inne odpowiedzi wydają się jednak o wiele bardziej „czyste”, nie wiem o nich.
— Mizipzor,

1
Wydaje mi się, że wszystko jest czyste, szczególnie jeśli odfiltrujeszisinstance(obj, types.ClassType)
— kojiro,


@ChrisSmith W szczególności dzisiaj odkryłem, że niektóre debuggery, takie jak pudburuchamianie programu w ten sposób, powodują, że kod ulega przypadkowemu uszkodzeniu sys.modulespodczas debugowania. globals()wydaje się trochę brzydka, ale wydaje się być bardziej niezawodna.
— Soren Bjornstad

15

Nie wiem, czy istnieje „właściwy” sposób, aby to zrobić, ale Twój fragment import fookodu jest na dobrej drodze: po prostu dodaj do foo.py, zrób inspect.getmembers(foo)i powinien działać dobrze.


Zaraz, pomyślałbym, że to stworzy zależność cykliczną czy coś, ale to działa!
— mcccclean

Powodem braku zależności cyklicznej lub pętli importu jest to, że po zaimportowaniu modułu jest on dodawany do globalnej przestrzeni nazw. Gdy importowany moduł jest wykonywany i przechodzi do „importu foo”, pomija import, ponieważ moduł jest już dostępny w globals. Jeśli wykonasz foo jako main (jako skrypt), moduł jest faktycznie uruchamiany dwukrotnie, ponieważ kiedy przejdziesz do 'import foo' main będzie w globalnej przestrzeni nazw, ale nie foo. Po „import foo” zarówno „ main ”, jak i „foo” znajdą się w globalnej przestrzeni nazw.
— galinden

10

Byłem w stanie uzyskać wszystko, czego potrzebowałem z dirwbudowanego plus 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)

Chociaż wygląda jak sierść:

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

Zauważ, że moduł przeglądarki klasy Python w stdlib wykorzystuje statyczną analizę źródła, więc działa tylko dla modułów, które są wspierane przez prawdziwy .pyplik.


4

Jeśli chcesz mieć wszystkie klasy należące do bieżącego modułu, możesz użyć tego:

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)

Jeśli użyjesz odpowiedzi Nadii i importujesz inne klasy do swojego modułu, klasy te również zostaną zaimportowane.

Właśnie dlatego member.__module__ == __name__jest dodawany do predykatu używanego na is_class_member. Ta instrukcja sprawdza, czy klasa naprawdę należy do modułu.

Predykat to funkcja (wywoływalna), która zwraca wartość logiczną.


3

Kolejne rozwiązanie, które działa w Pythonie 2 i 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()

To nie działa w 3.6.8. Nie dostaję błędu modułu.
— Aviral Srivastava

3

To jest linia, której używam, aby uzyskać wszystkie klasy, które zostały zdefiniowane w bieżącym module (tj. Nie są importowane). Według PEP-8 jest trochę długi, ale możesz go zmienić według własnego uznania.

import sys
import inspect

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

To daje listę nazw klas. Jeśli chcesz same obiekty klasy, po prostu zachowaj obj zamiast tego.

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

To było bardziej przydatne z mojego doświadczenia.



0

Myślę, że możesz zrobić coś takiego.

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())`

jeśli potrzebujesz własnych zajęć


0

Często piszę narzędzia wiersza poleceń, w których pierwszy argument ma odnosić się do jednej z wielu różnych klas. Na przykład ./something.py feature command —-arguments, gdzie Featurejest klasa i commandmetoda dla tej klasy. Oto podstawowa klasa, która ułatwia to.

Zakłada się, że ta klasa bazowa znajduje się w katalogu obok wszystkich jego podklas. Możesz wtedy zadzwonić, ArgBaseClass(foo = bar).load_subclasses()co zwróci słownik. Na przykład, jeśli katalog wygląda następująco:

  • arg_base_class.py
  • feature.py

Zakładając , że feature.pyimplementuje class Feature(ArgBaseClass), wówczas powyższe wywołanie load_subclasseszwróci { 'feature' : <Feature object> }. To samo kwargs( foo = bar) zostanie przekazane do Featureklasy.

#!/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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.