Reputation: 7916
I have classes like this:
class Parent(object):
_defaults = {
'key': 'value',
'key2': 'value',
}
class Child(Parent):
_defaults = {
'key2': 'value2',
'key3': 'value2',
}
When I create the child class, I want _defaults
to be set to the parent class _defaults
and then updated with the child _defaults
. So the actual resolved class should look like this:
class Child(object):
_defaults = {
'key': 'value',
'key2': 'value2',
'key3': 'value2',
}
Is this possible with a __metaclass__
constructor or something?
Upvotes: 2
Views: 1086
Reputation: 107287
Using a metaclass you need to do this in __new__
method, in order to apply the changes before instantiation. Since the __new__
method accepts cls, name, bases, namespace, **kwds
as argument you can simply access to parent object using bases
argument which is a tuple of all base classes then you can simply update your expected attribute.
class MyMetaClass(type):
def __new__(cls, name, bases, namespace, **kwds):
bases[0]._defaults.update(namespace['_defaults'])
result = type.__new__(cls, name, bases, dict(namespace))
return result
class Parent(object):
_defaults = {
'key': 'value',
'key2': 'value',
}
class Child(Parent, metaclass=MyMetaClass):
_defaults = {
'key2': 'value2',
'key3': 'value2',
}
Demo:
c = Child()
print(type(c).__bases__[0]._defaults)
{'key2': 'value2', 'key3': 'value2', 'key': 'value'}
Note that as @jsbueno mentioned this method has a big problem which is updating the parent class's _defaults
attribute at each instantiation. One approach for refusing this problem might be providing the meta class with a flag in order to update the parent only once.
Upvotes: 1
Reputation: 10359
Read the dictionary items directly, then update?
class Parent(object):
_defaults = {
'key': 'value',
'key2': 'value',
}
class Child(Parent):
_defaults = {k: v for k, v in Parent._defaults.items()}
_defaults.update({'key2': 'value2',
'key3': 'value2'})
testpar = Parent()
test = Child()
print(testpar._defaults)
print(test._defaults)
prints:
{'key2': 'value', 'key': 'value'}
{'key3': 'value2', 'key2': 'value2', 'key': 'value'}
in both 2.7 and 3.5
Upvotes: 1
Reputation: 309889
You can accomplish this using a collections.ChainMap
import collections
class Child(Parent):
_defaults = collections.ChainMap({
'key2': 'value2',
'key3': 'value2',
}, Parent._defaults)
Of course, you could write a metaclass to combine these things for you, but it would be tricky to figure out (in general) which maps should be combined with the maps on the superclasses and which should override...
Upvotes: 3