Python — динамическое множественное наследование

Я ищу совета по поводу дизайна моего кода.

Введение

У меня есть несколько классов, каждый из которых представляет один тип файла, например :MediaImageFile, MediaAudioFile и универсальный (, а также базовый класс )MediaGenericFile.

У каждого файла есть два варианта :Master и Version, поэтому я создал эти классы, чтобы определить их специфическое поведение. РЕДАКТИРОВАТЬ:Версия представляет собой измененный/обрезанный/обрезанный/и т.д. вариант мастер-файла. Он используется в основном для превью.

РЕДАКТИРОВАТЬ:Причина, по которой я хочу сделать это динамически, заключается в том, что это приложение должно быть многоразовым (, это приложение Django -), и поэтому должно быть легко реализовать другой подкласс MediaGenericFile без изменения исходного кода.

Что я хочу сделать

  1. Прежде всего, пользователь должен иметь возможность регистрировать собственные подклассы MediaGenericFile, не затрагивая исходный код.

  2. Является ли файл версией или мастером, легко (одно регулярное выражение )распознать по имени файла.

    /path/to/master.jpg                   -- master
    /path/to/.versions/master_version.jpg -- version
    
  3. Классы Master/Version используют некоторые методы/свойства MediaGenericFile, например, имя файла (, которое необходимо знать для создания новой версии ).

  4. MediaGenericFile расширяет LazyFile, который является просто ленивым объектом File.

Теперь мне нужно собрать его воедино…

Используемый дизайн

Прежде чем я начал кодировать функцию «версий», у меня был фабричный класс MediaFile, который возвращает соответствующий класс типа файла в соответствии с расширением :

>>> MediaFile('path/to/image.jpg')
<<< <MediaImageFile 'path/to/image.jpg'>

. Классы Master и Version определяют новые методы, которые используют методы и атрибуты MediaGenericFile и т. д.

Подход 1

Один из подходов заключается в создании динамически нового типа,который наследует Master (или Version )и MediaGenericFile (или подкласс ).

class MediaFile(object):
    def __new__(cls, *args, **kwargs):
       ...  # decision about klass
        if version:
            bases = (Version, klass)
            class_name = '{0}Version'.format(klass.__name__)
        else:
            bases = (Master, klass)
            class_name = '{0}Master'.format(klass.__name__)

        new_class = type(class_name, bases, {})
       ...
        return new_class(*args, **kwargs)

Подход 2

Второй подход — создать метод «внести _в экземпляр _» в Master/Version и вызвать его после создания нового класса _, но это сложнее, чем я думал:

classs Master(object):
    @classmethod
    def contribute_to_instance(cls, instance):
        methods = (...)
        for m in methods:
            setattr(instance, m, types.MethodType(getattr(cls, m), instance))

class MediaFile(object):
    def __new__(*args, **kwargs):
       ...  # decision about new_class
        obj = new_class(*args, **kwargs)
        if version:
            version_class = Version
        else:
            version_class = Master

        version_class.contribute_to_instance(obj)
       ...
        return obj

Однако это не работает. Все еще есть проблемы с вызовом методов Master/Version.

Вопросы

Что было бы хорошим способом реализовать это множественное наследование?

Как называется эта проблема? :)Я пытался найти некоторые решения, но просто не знаю, как назвать эту проблему.

Заранее спасибо!

Примечание к ответам

ад ларсманс

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

  1. Сравнения в любом случае переопределяются

    class MediaGenericFile(object):
        def __eq__(self, other):
            return self.name == other.name
    
  2. Мне никогда не нужно проверять isinstance (MediaGenericFileVersion, instance ). Я использую isinstance (MediaGenericFile, instance )и isinstance (Version, instance ), и оба работают должным образом.

Тем не менее, создание нового типа для каждого экземпляра звучит для меня значительным недостатком.

Ну, я мог бы динамически создавать оба варианта в метаклассе, а затем использовать их, что-то вроде:

>>> MediaGenericFile.version_class
<<< <class MediaGenericFileVersion>
>>> MediaGenericFile.master_class
<<< <class MediaGenericFileMaster>

А потом:

class MediaFile(object):
    def __new__(cls, *args, **kwargs):
       ...  # decision about klass
        if version:
            attr_name = 'version_class'
        else:
            attr_name = 'master_class'

    new_class = getattr(klass, attr_name)
   ...
    return new_class(*args, **kwargs)

Окончательное решение

Наконец, шаблон проектирования — это фабричный класс. Подклассы MediaGenericFile статически типизированы, пользователи могут реализовать и зарегистрировать свои собственные. Варианты Master/Version создаются динамически (, склеиваются вместе из нескольких примесей )в метаклассе и сохраняются в «кэше», чтобы избежать опасностей, упомянутых larsmans .

Спасибо всем за их предложения. Наконец-то я понимаю концепцию метакласса. Ну, по крайней мере, я думаю, что понимаю это. Отправить мастер источника…

8
задан Tomáš Ehrlich 22 July 2012 в 07:18
поделиться