Làm thế nào tôi có thể nhận được một danh sách tất cả các lớp trong mô-đun hiện tại trong Python?


301

Tôi đã thấy rất nhiều ví dụ về việc mọi người trích xuất tất cả các lớp từ một mô-đun, thường là một cái gì đó như:

# foo.py
class Foo:
    pass

# test.py
import inspect
import foo

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

Tuyệt vời.

Nhưng tôi không thể tìm ra cách lấy tất cả các lớp từ mô-đun hiện tại .

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

Đây có lẽ là một cái gì đó thực sự rõ ràng, nhưng tôi không thể tìm thấy bất cứ điều gì. Bất cứ ai có thể giúp tôi ra?


2
Có một PEP cho một tính năng như thế này, nhưng nó đã bị từ chối.
Gary van der Merwe

Có gì sai khi đọc nguồn cho "class"? Tại sao không làm việc đó?
S.Lott

66
Tôi đoán câu hỏi là muốn tự động hóa một số nhiệm vụ, vì vậy điều quan trọng là nó phải được thực hiện theo chương trình. Có lẽ người hỏi nghĩ rằng làm điều đó bằng tay, bằng cách đọc mã nguồn bằng mắt, có thể lặp đi lặp lại, dễ bị lỗi hoặc tốn thời gian.
Jonathan Hartley

Câu trả lời:


386

Thử cái này:

import sys
current_module = sys.modules[__name__]

Trong bối cảnh của bạn:

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

Và thậm chí tốt hơn:

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

Bởi vì inspect.getmembers()có một vị ngữ.


9
Nếu tôi nhập các lớp trong mô-đun này ở cấp mô-đun (tức là from optparse import OptionParser) , các mô-đun đó được bao gồm trong danh sách in. Làm thế nào tôi có thể tránh điều đó?
Chris

5
@phasetwenty, thay vì tests.is class bạn có thể có một cái gì đó như:inspect.getmembers(sys.modules[__name__], lambda member: member.__module__ == __name__ and isnpect.isclass)
Nadia Alramli

1
Nhưng dict(inspect.getmembers(sys.modules[__name__])) == globals()là luôn luôn True, vậy tại sao nhập khẩu?
kojiro

16
Câu trả lời của Nadia gần như đúng. Tốt hơn: inspect.getmembers(sys.modules[__name__], lambda member: inspect.isclass(member) and member.__module__ == __name__
William Budington

1
@ John. vì Nadia quên gọi isclass.
Alex Hall

20

Thế còn

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

?


Đây là những gì tôi thường làm. Các câu trả lời khác có vẻ "sạch" hơn nhiều, mặc dù không biết về chúng.
Mizipzor

1
Có vẻ rất sạch đối với tôi, đặc biệt nếu bạn lọc vàoisinstance(obj, types.ClassType)
kojiro

4
Tôi thích câu trả lời này tốt hơn vì nó sẽ hoạt động ngay cả khi mô-đun hiện tại chưa được đặt trong sys.modules, ví dụ: từ docs.python.org/2/l Library / fiances.html
Chris Smith

@ChrisSmith Đặc biệt, hôm nay tôi phát hiện ra rằng một số trình gỡ lỗi như pudbchạy chương trình của bạn theo cách này, dẫn đến việc sử dụng mã bị sys.modulesphá vỡ ngẫu nhiên trong khi gỡ lỗi. globals()có vẻ hơi xấu xí, nhưng có vẻ đáng tin cậy hơn nhiều.
Soren Bjornstad

15

Tôi không biết có cách nào phù hợp để thực hiện hay không, nhưng đoạn trích của bạn đang đi đúng hướng: chỉ cần thêm import foovào foo.py, làm inspect.getmembers(foo)và nó sẽ hoạt động tốt.


Whoa, tôi đã nghĩ rằng điều này sẽ tạo ra một phụ thuộc tròn hoặc một cái gì đó, nhưng nó hoạt động!
mcccclean

Lý do bạn không nhận được phụ thuộc vòng tròn hoặc vòng lặp nhập là vì khi bạn nhập mô-đun, nó sẽ được thêm vào không gian tên toàn cục. Khi mô-đun đã nhập được thực thi và được 'nhập foo', nó sẽ bỏ qua quá trình nhập vì mô-đun đã có sẵn trên toàn cầu. Nếu bạn thực thi foo là chính (dưới dạng tập lệnh), mô-đun thực sự được chạy hai lần vì khi bạn nhận được 'nhập foo' chính sẽ nằm trong không gian tên toàn cục nhưng không phải là foo. Sau 'nhập foo' cả ' chính ' và 'foo' sẽ ở trong không gian tên toàn cầu.
galinden

11

Tôi đã có thể nhận được tất cả những gì tôi cần từ tích dirhợp 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)

Mặc dù, nó trông giống như một quả bóng tóc:

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

Lưu ý rằng mô-đun trình duyệt lớp Python của stdlib sử dụng phân tích nguồn tĩnh, do đó, nó chỉ hoạt động cho các mô-đun được hỗ trợ bởi một .pytệp thực .


4

Nếu bạn muốn có tất cả các lớp, thuộc về mô-đun hiện tại, bạn có thể sử dụng điều này:

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)

Nếu bạn sử dụng câu trả lời của Nadia và bạn đang nhập các lớp khác trên mô-đun của mình, thì các lớp đó cũng sẽ được nhập.

Vì vậy, đó là lý do tại sao member.__module__ == __name__đang được thêm vào vị ngữ được sử dụng trên is_class_member. Câu lệnh này kiểm tra rằng lớp thực sự thuộc về mô-đun.

Một vị ngữ là một hàm (có thể gọi được), trả về giá trị boolean.


3

Một giải pháp khác hoạt động trong Python 2 và 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()

Điều này không hoạt động trong 3.6.8. Tôi không nhận được lỗi mô-đun.
Avirus Srivastava

3

Đây là dòng mà tôi sử dụng để có được tất cả các lớp đã được xác định trong mô-đun hiện tại (tức là không được nhập). Nó hơi dài theo PEP-8 nhưng bạn có thể thay đổi nó khi bạn thấy phù hợp.

import sys
import inspect

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

Điều này cung cấp cho bạn một danh sách các tên lớp. Nếu bạn muốn các đối tượng lớp mình chỉ cần giữ obj thay thế.

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

Điều này đã được hữu ích hơn trong kinh nghiệm của tôi.



0

Tôi nghĩ rằng bạn có thể làm một cái gì đó như thế này.

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

nếu bạn cần lớp học riêng


0

Tôi thường thấy mình viết các tiện ích dòng lệnh trong đó đối số đầu tiên có nghĩa là đề cập đến một trong nhiều lớp khác nhau. Ví dụ ./something.py feature command —-arguments, đâu Featurelà một lớp và commandlà một phương thức trên lớp đó. Đây là một lớp cơ sở làm cho điều này dễ dàng.

Giả định là lớp cơ sở này nằm trong một thư mục cùng với tất cả các lớp con của nó. Sau đó, bạn có thể gọi ArgBaseClass(foo = bar).load_subclasses()mà sẽ trả lại một từ điển. Ví dụ: nếu thư mục trông như thế này:

  • arg_base_group.py
  • tính năng

Giả sử feature.pythực hiện class Feature(ArgBaseClass), sau đó lời mời trên load_subclassessẽ trở lại { 'feature' : <Feature object> }. Cùng kwargs( foo = bar) sẽ được truyền vào Featurelớp.

#!/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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.