具有自定义元 class 的 class 的所有子 class 都共享相同的属性,即使它们不应该
All subclasses of a class with a custom metaclass share the same attributes even though they shouldn't
我定义了一个元class MyMeta
和一个名为MyAttributeType
的自定义属性。然后我创建了 4 个 classes,第一个 class MyObjectA
的 metaclass 属性设置为 MyMeta
,class MyObjectB
继承自 MyObjectA
并具有一些属性,然后 MyObjectC
和 MyObjectD
都继承自 MyObjectB
并且它们都具有一些额外的属性。
在 metaclass 中,我将这些属性添加到一个列表中,所以我希望该列表只包含 class 本身及其父项的属性,而不是它们都得到所有属性。
我认为查看代码更容易,下面是一个最小测试,您可以复制并 运行 它应该可以工作。远低于输出与预期输出。
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
输出:
['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']
预计:
[]
['attr2', 'attr3', 'attr1']
['attr2', 'attr3', 'attr1', 'attr6', 'attr5', 'attr4']
['attr2', 'attr3', 'attr1', 'attr7', 'attr8', 'attr9']
您需要分享基础 类 的 _attributes
作为 副本:
result_dict["_attributes"] = base.__dict__["_attributes"][:]
共享列表而不复制它意味着更改对所有引用可见。您目前正在执行与此等效的操作:
>>> base_attributes = []
>>> child_attributes = base_attributes
>>> child_attributes.append(2)
>>> base_attributes
[2]
使用[:]
创建赋值时所有元素的副本:
>>> base_attributes = []
>>> child_attributes = base_attributes[:]
>>> child_attributes.append(2)
>>> base_attributes
[]
我定义了一个元class MyMeta
和一个名为MyAttributeType
的自定义属性。然后我创建了 4 个 classes,第一个 class MyObjectA
的 metaclass 属性设置为 MyMeta
,class MyObjectB
继承自 MyObjectA
并具有一些属性,然后 MyObjectC
和 MyObjectD
都继承自 MyObjectB
并且它们都具有一些额外的属性。
在 metaclass 中,我将这些属性添加到一个列表中,所以我希望该列表只包含 class 本身及其父项的属性,而不是它们都得到所有属性。
我认为查看代码更容易,下面是一个最小测试,您可以复制并 运行 它应该可以工作。远低于输出与预期输出。
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
输出:
['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']
预计:
[]
['attr2', 'attr3', 'attr1']
['attr2', 'attr3', 'attr1', 'attr6', 'attr5', 'attr4']
['attr2', 'attr3', 'attr1', 'attr7', 'attr8', 'attr9']
您需要分享基础 类 的 _attributes
作为 副本:
result_dict["_attributes"] = base.__dict__["_attributes"][:]
共享列表而不复制它意味着更改对所有引用可见。您目前正在执行与此等效的操作:
>>> base_attributes = []
>>> child_attributes = base_attributes
>>> child_attributes.append(2)
>>> base_attributes
[2]
使用[:]
创建赋值时所有元素的副本:
>>> base_attributes = []
>>> child_attributes = base_attributes[:]
>>> child_attributes.append(2)
>>> base_attributes
[]