Как добиться наследования с помощью фабричных функций метода класса - PullRequest
0 голосов
/ 16 ноября 2018

Как использовать / повторно использовать реализации в родительском классе при использовании подхода 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 Ответ

0 голосов
/ 16 ноября 2018

Используйте 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, вот для чего оно.

...