0

How do I use/reuse implementations in the parent class when using the classmethod approach for implementing factory functions?

In the example below, class A is fine, but class B is broken.

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

The context is that I'm trying to instantiate instances based on JSON configuration data. The inheritance hierarchy is deeper than just two classes, i.e. there are a number of children of class B. The root of the inheritance hierarchy does some useful stuff in the factory function. Children classes should re-use that but add on some additional operations.

1
  • I've done this in the past through registration of children and making the parent discriminate between children for deserialization. Commented Nov 15, 2018 at 22:51

1 Answer 1

1

Use super, of course:

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 action:

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

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

Note, what you were trying to do won't work because @classmethod creates a descriptor that binds the class when called from either the class or the instance. You would have to access the raw function using something like:

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

To make it work, but just use super, that's what it is for.

Sign up to request clarification or add additional context in comments.

10 Comments

How to in python2.7 where arguments are required?
@Dave I edited in to a comment: # res = super(B, cls).from_jdata(data) # in python 2
it looks like super(B, cls).from_jdata(data) works
Why do you need to call super in init of class B? Could you not just update self.__dict__ in the init from class B itself?
@user007 you could, but it is better to re-use code instead of having to maintain the same code in two separate places
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.