Список классов в каталоге (Python) - PullRequest
4 голосов
/ 05 июля 2010

Я разрабатываю пакет Python 2.6, в котором я хотел бы получить список всех классов в определенном каталоге (в пакете), чтобы затем выполнить самоанализ объектов класса.

Специальноесли каталог, содержащий исполняемый в данный момент модуль, имеет подкаталог с именем 'foobar', а 'foobar' содержит файлы .py, указывающие class Foo(MyBase), class Bar(MyBase) и class Bar2, я хочу получить список ссылок наОбъекты класса, которые наследуются от MyBase, то есть Foo и Bar, но не Bar2.

Я не уверен, действительно ли для этой задачи нужно иметь дело с файловой системой или же с модулямив поддиректории автоматически загружаются и просто должны быть перечислены через самоанализ.Любые идеи здесь, пожалуйста?Пример кода очень ценится, так как я довольно новичок в Python, в частности, самоанализ.

Ответы [ 5 ]

4 голосов
/ 05 июля 2010

Модули никогда не загружаются автоматически, но должно быть легко перебирать модули в каталоге и загружать их с помощью встроенной функции __import__:

import os
import glob
for file in glob(os.path.join(os.path.dirname(os.path.abspath(__file__))), "*.py"):
    name = os.path.splitext(os.path.basename(file))[0]
    # add package prefix to name, if required
    module = __import__(name)
    for member in dir(module):
        # do something with the member named ``member``
3 голосов
/ 10 июля 2014

Я хотел сделать то же самое, вот чем я закончил:

import glob
import importlib
import inspect
import os

current_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)))
current_module_name = os.path.splitext(os.path.basename(current_dir))[0]
for file in glob.glob(current_dir + "/*.py"):
     name = os.path.splitext(os.path.basename(file))[0]

     # Ignore __ files
     if name.startswith("__"):
         continue
     module = importlib.import_module("." + name,package=current_module_name)

     for member in dir(module):
         handler_class = getattr(module, member)

         if handler_class and inspect.isclass(handler_class):
             print member

Надеюсь, это поможет ..

2 голосов
/ 05 июля 2010

Вариант 1: grep для регулярного выражения "^ class (\ a \ w +) \ (Myclass" с параметром -r.

Вариант 2: сделать каталог пакетом (создайте пустой файл __init__.py), импортируйте его и рекурсивно проведите по его элементам:

import mymodule
def itermodule(mod):
    for member in dir(mymod):
        ...

itermodule(mymodule)
1 голос
/ 19 мая 2016

Разобраться с этим сам, это моя версия (раздвоенный фрагмент @krakover):

  • Итерация каталога и импорт каждого скрипта, размещенного там
    • Отфильтровывать абстрактные классы
    • Отфильтровать классы, которые не наследуют базовый класс
    • Новый экземпляр для каждого повторяющегося класса (измените его, если он не окажется полезным)

import importlib
import inspect
import os
import glob


def import_plugins(plugins_package_directory_path, base_class=None, create_instance=True, filter_abstract=True):

    plugins_package_name = os.path.basename(plugins_package_directory_path)

    # -----------------------------
    # Iterate all python files within that directory
    plugin_file_paths = glob.glob(os.path.join(plugins_package_directory_path, "*.py"))
    for plugin_file_path in plugin_file_paths:
        plugin_file_name = os.path.basename(plugin_file_path)

        module_name = os.path.splitext(plugin_file_name)[0]

        if module_name.startswith("__"):
            continue

        # -----------------------------
        # Import python file

        module = importlib.import_module("." + module_name, package=plugins_package_name)

        # -----------------------------
        # Iterate items inside imported python file

        for item in dir(module):
            value = getattr(module, item)
            if not value:
                continue

            if not inspect.isclass(value):
                continue

            if filter_abstract and inspect.isabstract(value):
                continue

            if base_class is not None:
                if type(value) != type(base_class):
                    continue

            # -----------------------------
            # Instantiate / return type (depends on create_instance)

            yield value() if create_instance else value

Использование:

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
plugins_directory_path = os.path.join(SCRIPT_DIR, 'plugins')
plugins = import_plugins(plugins_directory_path, base_class=BasePlugin)

for plugin in plugins:
    plugin.foo()
  • представьте себе, что есть подкаталог с именем plugins, содержащий реализации BasePlugin класса
0 голосов
/ 21 июля 2013

На платформах с egrep:

from subprocess import Popen, PIPE
from re import search

def get_classes(directory):
    job = Popen(['egrep', '-ir', '--include=*.py', 'class ', str(directory), ], stdout=PIPE)
    fileout, fileerr = job.communicate()
    if fileerr:
        raise Exception(fileerr)
    while directory[-1] == '/':
        directory = directory[:-1]
    found = []
    for line in fileout.split('\n'):
        match = search('^([^:]+).py:\s*class\s*(\S+)\s*\((\S+)\):', line)
        if match:
            pypath = match.group(1).replace(directory, '').replace('/', '.')[1:]
            cls = match.group(2)
            parents = filter(lambda x: x.strip, match.group(3).split())
            found.append((pypath, cls, parents, ))
    return found

Для get_classes('.') egrep возвращает что-то вроде:

./helpers/action.py:class Action(object):
./helpers/get_classes.py:    job = Popen(['egrep', '-ir', '--include=*.py', 'class ', str(directory), ], stdout=PIPE) # this is the get_classes script; not a valid result
./helpers/options.py:class Option(object):

, которое преобразуется в кортежи пути, имени класса и прямойпредки:

[('helpers.action', 'Action', ['object']), ('helpers.options', 'Option', ['object'])]

Если вы просто хотите пути, это [item[0] for item in get_classes('.')].

...