ChatGPT解决这个技术问题 Extra ChatGPT

How can I get a list of all classes within current module in Python?

I've seen plenty of examples of people extracting all of the classes from a module, usually something like:

# foo.py
class Foo:
    pass

# test.py
import inspect
import foo

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

Awesome.

But I can't find out how to get all of the classes from the current module.

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

This is probably something really obvious, but I haven't been able to find anything. Can anyone help me out?

There was a PEP for a feature like this, but it was rejected.
What's wrong with reading the source for "class"? Why won't that work?
I'm guessing the question is about wanting to automate some task, so it's important that it be done programmatically. Presumably the questioner thinks that doing it manually, by reading the source code with your eyes, might be repetitive, error-prone or time-consuming.
A simple one liner I created for inspecting a class based on this question is: [name for name,obj in inspect.getmembers(foo) if inspect.isclass(obj)]

k
kenorb

Try this:

import sys
current_module = sys.modules[__name__]

In your context:

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

And even better:

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

Because inspect.getmembers() takes a predicate.


If I import classes in this module at the module level (i.e., from optparse import OptionParser) those modules are included in the print list. How might I avoid that?
@phasetwenty, instead of inspect.isclass you can have something like: inspect.getmembers(sys.modules[__name__], lambda member: member.__module__ == __name__ and isnpect.isclass)
but dict(inspect.getmembers(sys.modules[__name__])) == globals() is always True, so why the imports?
Nadia's answer is almost correct. Better: inspect.getmembers(sys.modules[__name__], lambda member: inspect.isclass(member) and member.__module__ == __name__
@JohnM. because Nadia forgot to call isclass.
K
Krab

What about

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

?


This is what I usually do. The other answers seems much more "clean" though, didnt know about them.
Seems plenty clean to me, particularly if you filter on isinstance(obj, types.ClassType)
I like this answer better because it will work even if the current module hasn't been put in sys.modules, e.g. from docs.python.org/2/library/functions.html#execfile
@ChrisSmith In particular, I discovered today that some debuggers such as pudb run your program this way, which results in code using sys.modules breaking randomly while debugging it. globals() seems a little bit ugly, but it seems to be much more reliable.
i
int3

I don't know if there's a 'proper' way to do it, but your snippet is on the right track: just add import foo to foo.py, do inspect.getmembers(foo), and it should work fine.


Whoa, I would have thought this would create a circular dependency or something, but it works!
The reason you don't get a circular dependency or an import loop is that once you import a module it's added to the global namespace. When the imported module is executed and gets to 'import foo' it skips the import because the module is already available in globals. If you execute foo as main (as a script) the module is actually run twice because when you get to 'import foo' main will be in the global namespace but not foo. After 'import foo' both 'main' and 'foo' will be in the globals namespace.
T
ThorSummoner

I was able to get all I needed from the dir built in 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)

Though, it does come out looking like a hairball:

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

n
ncoghlan
import pyclbr
print(pyclbr.readmodule(__name__).keys())

Note that the stdlib's Python class browser module uses static source analysis, so it only works for modules that are backed by a real .py file.


B
Benjy Malca

If you want to have all the classes, that belong to the current module, you could use this :

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)

If you use Nadia's answer and you were importing other classes on your module, that classes will be being imported too.

So that's why member.__module__ == __name__ is being added to the predicate used on is_class_member. This statement checks that the class really belongs to the module.

A predicate is a function (callable), that returns a boolean value.


A
Austin Mackillop

This is the line that I use to get all of the classes that have been defined in the current module (ie not imported). It's a little long according to PEP-8 but you can change it as you see fit.

import sys
import inspect

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

This gives you a list of the class names. If you want the class objects themselves just keep obj instead.

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

This is has been more useful in my experience.


F
Florian

Another solution which works in Python 2 and 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()

This does not work in 3.6.8. I get no module error.
佚名

I think that you can do something like this.

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

if you need own classes


Z
Zane Claes

I frequently find myself writing command line utilities wherein the first argument is meant to refer to one of many different classes. For example ./something.py feature command —-arguments, where Feature is a class and command is a method on that class. Here's a base class that makes this easy.

The assumption is that this base class resides in a directory alongside all of its subclasses. You can then call ArgBaseClass(foo = bar).load_subclasses() which will return a dictionary. For example, if the directory looks like this:

arg_base_class.py

feature.py

Assuming feature.py implements class Feature(ArgBaseClass), then the above invocation of load_subclasses will return { 'feature' : <Feature object> }. The same kwargs (foo = bar) will be passed into the Feature class.

#!/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

U
U12-Forward
import Foo 
dir(Foo)

import collections
dir(collections)

dir() will contain imported classes too, moreover you can't determine if its an imported or a defined class
c
cmarkle

The following can be placed at the top of the file:

def get_classes():
    import inspect, sys
    return dict(inspect.getmembers(
        sys.modules[__name__],
        lambda member: inspect.isclass(member) and member.__module__ == __name__
    ))

Note, this can be placed at the top of the module because we've wrapped the logic in a function definition. If you want the dictionary to exist as a top-level object you will need to place the definition at the bottom of the file to ensure all classes are included.


s
sipreeti

Go to Python Interpreter. type help ('module_name') , then press Enter. e.g. help('os') . Here, I've pasted one part of the output below:

class statvfs_result(__builtin__.object)
     |  statvfs_result: Result from statvfs or fstatvfs.
     |
     |  This object may be accessed either as a tuple of
     |    (bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax),
     |  or via the attributes f_bsize, f_frsize, f_blocks, f_bfree, and so on.
     |
     |  See os.statvfs for more information.
     |
     |  Methods defined here:
     |
     |  __add__(...)
     |      x.__add__(y) <==> x+y
     |
     |  __contains__(...)
     |      x.__contains__(y) <==> y in x