3
\$\begingroup\$

I have a hierarchy of entities that should contain dictionaries with metadata. Any sub-class should inherit its ancestor dictionary and be able to add some key-values (delete inherit keys is not needed). The easiest solution I can think about is this:

class A(object):
    def __init__(self):
        self.doc = A.generate_doc()

    @staticmethod
    def generate_doc():
         return {'a': 0}

class B(A):
    def __init__(self):
        super().__init__()
        self.doc.update(B.generate_doc())

    @staticmethod
    def generate_doc():
        return {'b': 0}

class C(B):
    def __init__(self):
        super().__init__()
        self.doc.update(C.generate_doc())

    @staticmethod
    def generate_doc():
        return {'c': 0}

print(A().doc)  # {'a': 0}
print(B().doc)  # {'a': 0, 'b': 0}
print(C().doc)  # {'a': 0, 'b': 0, 'c': 0}

Is this a good design? Could the update() blocks be implicit maybe? I have more than one document in real code, so that would be nice.

\$\endgroup\$
1
\$\begingroup\$

Is this a good design?

No IMO it's quite horrible, please reconsider it.

Could the update() blocks be implicit maybe?

Yeah, use metaclasses.

I have more than one document in real code, so that would be nice.

Yeah, don't request us to write code for you. However, with a metaclass this is easy.


To make a cross Python 2 and Python 3 metaclass you need to use something like Class = Class('name', (object,), {}). You want to also build these dictionaries in the metaclasses __new__, and for the values to be taken from another attribute. To do this I defaulted to the uppercase of the value. I.e. A.DOC.

This works kinda like Python's default multiple inheritance, but I make no guarantees. Also doc is now a static value, so you may need to clone it at object instantiation.

def update(values=[]):
    if isinstance(values, str):
        values = values.split()
    class Meta(type):
        def __new__(meta, classname, bases, class_dict):
            for value in values:
                val = {}
                for base in reversed(bases):
                    val.update(getattr(base, value, {}))
                val.update(class_dict.get(value.upper(), {}))
                class_dict[value] = val
            return super(Meta, meta).__new__(meta, classname, bases, class_dict)
    return Meta('Meta', (object,), {})


class A(update('doc')):
    DOC = {'v': 0}

class B(A):
    DOC = {'v': 1}

class C(A):
    DOC = {'c': 0}

class D(B, C):
    pass

class E(C, B):
    pass

print(D.doc)
print(E.doc)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.