Как добиться наследования с помощью фабричных функций метода класса

Как использовать / повторно использовать реализации в родительском классе при использовании classmethod подход для реализации фабричных функций?

В приведенном ниже примере class A хорошо, но class B сломано.

class A(object):
    def __init__(self, **kwds):
        self.__dict__.update(kwds)
    @classmethod
    def from_jdata(cls, data):
        if '_id' in data:
            data['uuid'] = data['_id']
            del data['_id']
        return cls(**data)

class B(A):
    def __init__(self, **kwds):
        super(B, self).__init__(**kwds)
    @classmethod
    def from_jdata(cls, data):
        # goal: make an instance of B, 
        # using the logic that is implemented in A.from_jdata
        # But does some extra stuff, akin to:
        res = A.from_jdata(B, data)
        res.__dict__['extra']='set'
        return res

Контекст заключается в том, что я пытаюсь создать экземпляры на основе данных конфигурации JSON. Иерархия наследования более глубока, чем просто два класса, т.е. class B, Корень иерархии наследования делает некоторые полезные вещи в функции фабрики. Детские классы должны использовать это повторно, но добавить некоторые дополнительные операции.

1 ответ

Решение

Использование super, конечно:

class A(object):
    def __init__(self, **kwds):
        self.__dict__.update(kwds)
    @classmethod
    def from_jdata(cls, data):
        if '_id' in data:
            data['uuid'] = data['_id']
            del data['_id']
        return cls(**data)

class B(A):
    def __init__(self, **kwds):
        super(B, self).__init__(**kwds)
    @classmethod
    def from_jdata(cls, data):
        # goal: make an instance of B,
        # using the logic that is implemented in A.from_jdata
        # But does some extra stuff, akin to:
        res = super().from_jdata(data)
        # res = super(B, cls).from_jdata(data) # in python 2
        res.__dict__['extra']='set'
        return res

В бою:

In [6]: b = B.from_jdata({'_id':42, 'foo':'bar'})

In [7]: vars(b)
Out[7]: {'foo': 'bar', 'uuid': 42, 'extra': 'set'}

Обратите внимание, что вы пытались сделать не получится, потому что @classmethod создает дескриптор, который связывает класс при вызове из класса или экземпляра. Вам нужно было бы получить доступ к сырой функции, используя что-то вроде:

res = A.__dict__['from_jdata'].__func__(B, data)

Чтобы заставить это работать, но просто используйте superвот для чего это.

Другие вопросы по тегам