Reputation: 239
I have defined a metaclass MyMeta
and a custom attribute called MyAttributeType
. Then I create 4 classes, the first, class MyObjectA
has the metaclass attribute set to MyMeta
, class MyObjectB
inherits from MyObjectA
and has a few attributes, then both MyObjectC
and MyObjectD
inherit from MyObjectB
and they both have some extra attribute.
In the metaclass I add those attributes to a list, so I would expect that list to contain only the attributes of the class itself and of its parents, instead they all get all the attributes.
I think it is easier to look at the code, below is a minimal test, you can copy and run it and should work. Far below is the output vs the expected output.
class MyAttributeType(object):
def __init__(self, name=None, node=None):
self._name = name
self._node = node
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def node(self):
return self._node
@node.setter
def node(self, value):
self._node = value
class MyMeta(type):
def __new__(mcs, clsname, clsbases, clsattributes):
result_dict = {"_attributes": []}
for base in clsbases:
try:
for key, value in base.__dict__.items():
if isinstance(value, property) or isinstance(value, MyAttributeType):
result_dict[key] = value
result_dict["_attributes"] = base.__dict__["_attributes"]
except KeyError:
pass
for key, value in clsattributes.items():
if isinstance(value, MyAttributeType):
if key.startswith("_"):
key = key[1:]
result_dict["_{0}".format(key)] = value
result_dict["_attributes"].append(value)
value.name = key
result_dict[key] = value
else:
result_dict[key] = value
inst = super(MyMeta, mcs).__new__(mcs, clsname, clsbases, result_dict)
return inst
class MyObjectA(object):
__metaclass__ = MyMeta
_name = None
_attributes = []
def __init__(self, name):
self._name = name
for attr in self._attributes:
attr.node = self._name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def attributes(self):
return [attr.name for attr in self._attributes]
class MyObjectB(MyObjectA):
attr1 = MyAttributeType()
attr2 = MyAttributeType()
attr3 = MyAttributeType()
class MyObjectC(MyObjectB):
attr4 = MyAttributeType()
attr5 = MyAttributeType()
attr6 = MyAttributeType()
class MyObjectD(MyObjectB):
attr7 = MyAttributeType()
attr8 = MyAttributeType()
attr9 = MyAttributeType()
a = MyObjectA("testA")
b = MyObjectB("testB")
c = MyObjectC("testC")
d = MyObjectD("testD")
print a.attributes
print b.attributes
print c.attributes
print d.attributes
Output:
['attr2', 'attr3', 'attr1', 'attr6', 'attr5', 'attr4', 'attr7', 'attr8', 'attr9']
['attr2', 'attr3', 'attr1', 'attr6', 'attr5', 'attr4', 'attr7', 'attr8', 'attr9']
['attr2', 'attr3', 'attr1', 'attr6', 'attr5', 'attr4', 'attr7', 'attr8', 'attr9']
['attr2', 'attr3', 'attr1', 'attr6', 'attr5', 'attr4', 'attr7', 'attr8', 'attr9']
Expected:
[]
['attr2', 'attr3', 'attr1']
['attr2', 'attr3', 'attr1', 'attr6', 'attr5', 'attr4']
['attr2', 'attr3', 'attr1', 'attr7', 'attr8', 'attr9']
Upvotes: 0
Views: 149
Reputation: 52159
You need to share _attributes
of the base classes as a copy:
result_dict["_attributes"] = base.__dict__["_attributes"][:]
Sharing a list without copying it means changes are visible on all references. You are currently doing the equivalent of this:
>>> base_attributes = []
>>> child_attributes = base_attributes
>>> child_attributes.append(2)
>>> base_attributes
[2]
Use [:]
to create a copy of all elements on assignment:
>>> base_attributes = []
>>> child_attributes = base_attributes[:]
>>> child_attributes.append(2)
>>> base_attributes
[]
Upvotes: 2