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.

asked May 22, 2017 at 7:10
\$\endgroup\$

1 Answer 1

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)
answered May 22, 2017 at 10:24
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.