Python'daki mevcut modül içindeki tüm sınıfların listesini nasıl alabilirim?


301

Bir modülden tüm sınıfları çıkaran birçok insan örneği gördüm, genellikle şöyle bir şey:

# foo.py
class Foo:
    pass

# test.py
import inspect
import foo

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

Muhteşem.

Ama tüm sınıfları mevcut modülden nasıl alacağımı bulamıyorum .

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

Bu muhtemelen çok açık bir şey, ama hiçbir şey bulamadım. Biri bana yardım edebilir mi?


2
Böyle bir özellik için bir PEP vardı , ancak reddedildi.
Gary van der Merwe

Kaynağı okumakta yanlış olan ne "class"? Neden işe yaramıyor?
S.Lott

66
Sanırım soru, bazı görevleri otomatikleştirmek istemekle ilgili, bu yüzden programlı olarak yapılması önemlidir. Muhtemelen soru soran, kaynak kodunu gözlerinizle manuel olarak yapmanın tekrarlayan, hataya açık veya zaman alıcı olabileceğini düşünmektedir.
Jonathan Hartley

Yanıtlar:


386

Bunu dene:

import sys
current_module = sys.modules[__name__]

Bağlamınızda:

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

Ve daha da iyisi:

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

Çünkü inspect.getmembers()bir yüklem alır.


9
Bu modülde sınıfları modül düzeyinde içe aktarırsam (yani from optparse import OptionParser), bu modüller yazdırma listesine dahil edilir. Bundan nasıl kaçınabilirim?
Chris

5
@phasetwenty, inspect.isclass yerine şöyle bir şey olabilir:inspect.getmembers(sys.modules[__name__], lambda member: member.__module__ == __name__ and isnpect.isclass)
Nadia Alramli

1
ama dict(inspect.getmembers(sys.modules[__name__])) == globals()her zaman True, neden ithalat?
kojiro

16
Nadia'nın cevabı neredeyse doğrudur. Daha iyi: inspect.getmembers(sys.modules[__name__], lambda member: inspect.isclass(member) and member.__module__ == __name__
William Budington

1
@JohnM. çünkü Nadia aramayı unuttu isclass.
Alex Hall

20

Ne dersin

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

?


Genellikle yaptığım budur. Diğer cevaplar çok daha temiz görünüyor olsa da, onlar hakkında bilmiyordum.
Mizipzor

1
Özellikle filtrelerseniz, bana çok temiz görünüyorisinstance(obj, types.ClassType)
kojiro

4
Bu yanıtı daha çok seviyorum çünkü mevcut modül sys.modules içine yerleştirilmemiş olsa bile çalışacaktır, örneğin docs.python.org/2/library/functions.html#execfile
Chris Smith

@ChrisSmith Özellikle, pudbprogramınızı bu şekilde çalıştırmak gibi bazı hata ayıklayıcıların , sys.moduleshata ayıklama sırasında rasgele kırılma kodunun kullanılmasına neden olduğunu keşfettim . globals()biraz çirkin gözüküyor, ama çok daha güvenilir görünüyor.
Soren Bjornstad

15

Bunu yapmanın 'uygun' bir yolu olup olmadığını bilmiyorum, ancak snippet'iniz doğru yolda: sadece import foofoo.py, do'ya ekleyin inspect.getmembers(foo)ve iyi çalışmalıdır.


Vay, bunun dairesel bir bağımlılık yaratacağını düşünürdüm, ama işe yarıyor!
mcccclean

Dairesel bir bağımlılık veya alma döngüsü almamanızın nedeni, bir modülü içe aktardığınızda genel ad alanına eklenmesidir. İçe aktarılan modül yürütüldüğünde ve 'import foo'ya ulaştığında, modül globallerde zaten mevcut olduğu için içe aktarmayı atlar. Eğer foo'yu main (komut dosyası olarak) olarak çalıştırırsanız, modül aslında iki kez çalıştırılır çünkü 'foo'yu içe aktardığınızda, main global isim alanında olur, ancak foo olmaz. 'Import foo' sonrasında hem ' main ' hem de 'foo' global isim alanında olacak.
galinden

10

İhtiyacım olan her şeyi diryerleşik artıdan alabildim 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)

Yine de, bir kıl yumağı gibi görünüyor:

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

Stdlib'in Python sınıfı tarayıcı modülünün statik kaynak analizi kullandığını, bu nedenle yalnızca gerçek bir .pydosya tarafından desteklenen modüller için çalıştığını unutmayın .


4

Geçerli modüle ait olan tüm sınıflara sahip olmak istiyorsanız, bunu kullanabilirsiniz:

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)

Nadia'nın cevabını kullanırsanız ve modülünüze başka sınıflar aktarıyorsanız, bu sınıflar da içe aktarılır.

Bu yüzden member.__module__ == __name__kullanılan yükleme ekleniyor is_class_member. Bu ifade, sınıfın gerçekten modüle ait olup olmadığını kontrol eder.

Bir yüklem, bir boole değeri döndüren bir işlevdir (çağrılabilir).


3

Python 2 ve 3'te çalışan başka bir çözüm:

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

Bu 3.6.8 'de çalışmaz. Modül hatası almıyorum.
Aviral Srivastava

3

Bu, geçerli modülde (yani içe aktarılmamış) tanımlanan tüm sınıfları almak için kullandığım satırdır. PEP-8'e göre biraz uzun ama uygun gördüğünüz gibi değiştirebilirsiniz.

import sys
import inspect

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

Bu size sınıf adlarının bir listesini verir. Sınıf nesneleri kendileri istiyorsanız sadece obj tutmak.

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

Bu benim deneyimimde daha yararlı oldu.



0

Böyle bir şey yapabileceğinizi düşünüyorum.

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

eğer kendi sınıflarına ihtiyacın varsa


0

Kendimi sık sık ilk argümanın birçok farklı sınıftan birine atıfta bulunduğu komut satırı yardımcı programları yazarken buluyorum. Örneğin , bir sınıf ./something.py feature command —-argumentsnerede Featureve commandbu sınıfta bir yöntemdir. İşte bunu kolaylaştıran bir temel sınıf.

Varsayım, bu temel sınıfın, tüm alt sınıflarının yanında bir dizinde yer almasıdır. Daha sonra ArgBaseClass(foo = bar).load_subclasses()bir sözlük döndürecek arayabilirsiniz . Örneğin, dizin şöyle görünüyorsa:

  • arg_base_class.py
  • feature.py

Varsayarsak feature.pyuygular class Feature(ArgBaseClass), sonra da yukarıdaki çağırma load_subclassesdönecektir { 'feature' : <Feature object> }. Aynı kwargs( foo = bar) Featuresınıfa geçirilir .

#!/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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.