Как динамически создавать новые объекты во время выполнения без их жесткого кодирования?

#python #class #oop

#python #класс #ооп

Вопрос:

Я новичок в python. Я пишу тестовое приложение для другого аналитического инструмента. Этот инструмент выводит различные типы файлов, которые мы называем «артефактами». Каждый тип артефакта представляет собой некоторый файл данных.

Я создал родительский класс с именем Artifact, который имеет функции load() и compare() .

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

Реализация этого мне понятна.

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

Новым дочерним элементом артефакта должен быть файл .py, но я бы не хотел изменять код основного тестового приложения. Поэтому я бы хотел, чтобы пользователи указывали в файле конфигурации использовать существующие артефакты куба и отчета, а также их вновь определенный артефакт. Для всех артефактов выполните load() и compare()

Могу ли я сделать это без явного импорта нового класса и без указания явного вызова для него?

Могу ли я просто написать

 for c in Artifact classes:
    c.load()
    c.compare()
  

? (это явно упрощено, я надеюсь, это понятно)

Комментарии:

1. Итак, по сути, вы хотите ссылаться на классы (которые наследуются от Artifact ) на основе пользовательского ввода? Что такое my_class = ./CustomArtifact.py ? И ВАШ код должен был бы работать с этим классом, а не наоборот?

2. Не описывайте свой код, покажите его нам!

3. Вы хотите загружать модули динамически; ознакомьтесь с importlib модулем.

4. @CollinHeist — Да, это идея.

Ответ №1:

Самый простой способ сделать это — добавить подпакет в ваш проект, где новые процессоры артефактов регистрируются как новый файл .py. Добавьте импорт __init__.py , и он загрузится автоматически.

 myproject
    __init__.py
    artifacts.py
    artifactsdb
        __init__.py
        cube.py
        report.py
  

artifacts.py

 class Artifacts:

    def load(self):
        pass

    def compare(self):
        pass

def enum_artifacts():
    for art in Artifacts.__subclasses__():
        art().load()
        art().compare()
  

artifactsdb/init.py

 from .cube import cube
from .report import report
  

artifacts/cube.py

 from ..artifacts import Artifacts
class cube(Artifacts):
    pass
  

Комментарии:

1. Хм .. хорошая идея, но, поскольку вы их написали compare , and load не являются методами класса, а art in for art in Artifacts.__subclasses__ — это класс, а не экземпляр. Просто создайте obj = art() и вызовите методы на obj

2. @Pynchia — Хм, OP не дает четкого определения того, что load и compare должно быть, поэтому я просто набросал что-то неопределенное. Например, как код должен выбирать, какой подкласс использовать? Какое-то правило об именах файлов? Универсальный загрузчик, который затем выбирает подтипы? Попробуйте их все, пока один не выйдет из строя? Я не знаю, поэтому просто набросал его. Я забыл вызвать __subclass__ пример. Я провел тестирование, но, похоже, пропустил несколько битов и ошибок, пока собирал их вместе.

3. Спасибо вам обоим. Я думаю, что в целом понимаю это предложение, но мне нужно будет попытаться реализовать это, чтобы справиться с этим. Мой python еще недостаточно силен, чтобы просто визуализировать это. Но это очень полезное предложение.

Ответ №2:

Конфигурационный файл может быть модулем python, который импортирует новые классы артефактов. Если ваши пользователи могут писать свои собственные классы python, у них не должно возникнуть проблем с редактированием такого конфигурационного файла:

  • в userconfig.py:
 # import user artifact classes:
from myartifact import NewArtifact

# add user artifacts to the list "user_artifacts"
user_artifacts = [
    NewArtifact
]
  
  • в вашем основном коде:
 import userconfig

for c in userconfig.user_artifacts:
    # load and compare