Как наследоваться от класса, созданного с помощью конструктора?

У меня есть класс Document, этот класс действительно сложно создать, поэтому у меня есть объект-конструктор для их создания. Оба элемента не мои, поэтому я не могу их изменить

Теперь я хочу создать подкласс Document, просто чтобы добавить некоторые определенные методы. Чтобы продолжать использовать предоставленный конструктор, я попробовал это:

class SpecialDocument(Document):
     def __new__(cls, *args):
         return DocumentBuilder(*args)

     def __init__(self, *args, **kwargs):
         #My initialization

Проблема здесь в том, что метод __init__ никогда не выполняется, потому что метод __new__ не возвращает SpecialDocument (он возвращает Document)

В моем конкретном случае мне не нужно строить свой SpecialDocument иначе, чем я строю Document. Есть ли способ использовать тот же конструктор? Если нет, то как я могу этого добиться? Я просто хочу унаследовать от Document, чтобы добавить определенные функции, возможно, этого можно было бы достичь с помощью метаклассов, но я никогда их не использовал (вероятно, потому, что я не совсем понимаю это), было бы неплохо немного узнать о них, если это может помочь решить мою проблему. проблема


person Mr. E    schedule 18.10.2016    source источник
comment
Вам действительно нужен подкласс? Разве вы не можете просто использовать делегирование, чтобы добавить только то поведение, которое вы хотите?   -  person Bakuriu    schedule 19.10.2016
comment
Какая связь между Document и DocumentBuilder?   -  person Philip Tzou    schedule 19.10.2016
comment
@PhilipTzou DocumentBuilder — это независимый класс, который возвращает новый объект Document, если он построен без параметров, или инициирует свои переменные, если он получает параметр (параметр — это путь к фактическому документу, если он существует)   -  person Mr. E    schedule 19.10.2016
comment
@Bakuriu Я выбрал вариант подкласса, потому что у меня есть несколько SpecialDocument, и все они имеют общее поведение Document, но если вы считаете, что делегирование - лучший вариант, я был бы рад принять его.   -  person Mr. E    schedule 19.10.2016
comment
Ну, у вас может быть общий класс MyBaseDocument, который содержит Document в качестве атрибута и делегирует большую часть функциональности документа, а затем позволяет другим специализированным подклассам наследовать от этого класса.   -  person Bakuriu    schedule 19.10.2016
comment
@Bakuriu Но в этом случае, если у Document есть метод с именем write_title, то MyBaseDocument понадобится какой-то метод, например def write_title(self, *args): self.document.write_title(*args), верно? Я хотел избежать этого, потому что существует множество методов, или есть способ указать интерпретатору Python всегда вызывать метод в self.document без явного написания его для каждого из них? Возможно, используется getattr, но это выглядит немного противно   -  person Mr. E    schedule 19.10.2016
comment
@Mr.E Это зависит. Имейте в виду, что вы можете использовать __getattr__. Что-то вроде def __getattr__(self, name): return lambda *args: getattr(self._document, name)(*args) могло бы избежать написания подобных методов. Метод __getattr__ вызывается всякий раз, когда кто-то ищет атрибут и атрибут не найден. Если затем подкласс хочет предоставить пользовательскую реализацию, он может просто явно реализовать этот метод.   -  person Bakuriu    schedule 19.10.2016


Ответы (1)


На самом деле здесь вам не нужен метакласс - вам просто нужно правильно вызвать метод __new__ суперкласса. То, как вы это делаете, создание экземпляра суперкласса вообще не «знает», что он вызывается из подкласса.

Итак, просто напишите свой код следующим образом:

class SpecialDocument(Document):
     def __new__(cls, *args):
         return super().__new__(cls, *args)

     def __init__(self, *args, **kwargs):
         #My initialization

Теперь это обычный способ сделать это, и он сработает, если код в вашей функции "строитель" будет правильно размещен внутри __new__ или __init__ документа.
Поскольку код этого не делает, и вы не можете [ не передать ваш подкласс в качестве параметра компоновщика, рабочим решением может быть создание обычного документа и замена его класса после его создания:

def special_document_init(special_document):
    ...

class SpecialDocument(Document):
    def my_special_method(self, ...):
         ...
    def overriden_method(self):
         ...
         result = super().overriden_method()
         ...

def build_special_document(*args):
    document = DocumentBuilder(*args)
    document.__class__ = SpecialDocument
    special_document_init(document)
    return document
person jsbueno    schedule 05.12.2016