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.
1 Answer 1
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)