具有自定义元类的类的所有子类共享相同的属性,即使它们不应该
Posted
技术标签:
【中文标题】具有自定义元类的类的所有子类共享相同的属性,即使它们不应该【英文标题】:All subclasses of a class with a custom metaclass share the same attributes even though they shouldn't 【发布时间】:2020-01-09 12:25:34 【问题描述】:我定义了一个元类MyMeta
和一个名为MyAttributeType
的自定义属性。然后我创建了4个类,第一个类MyObjectA
的元类属性设置为MyMeta
,类MyObjectB
继承自MyObjectA
并有一些属性,然后MyObjectC
和MyObjectD
都继承自@ 987654329@,它们都有一些额外的属性。
在元类中,我将这些属性添加到列表中,因此我希望该列表仅包含类本身及其父类的属性,而不是它们都获取所有属性。
我认为看代码更容易,下面是一个最小的测试,你可以复制并运行它,应该可以工作。远低于预期的输出。
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']
【问题讨论】:
没有必要明确指出您的版本,因为您的出版物表明了您上一版本的日期和时间,并且有它的历史。 “下面是一个最小的测试”恐怕不是最小的。从您的输出来看,两个类就足够了,名称字段、属性和赋值完全是多余的,而且大多数元类__new__
也不需要。 FWIF,@987654334@ 确保您在所有实例之间都有一个共享列表 - 您是否认为这是必要的而不是问题的原因?
我添加它的原因是没有我没有得到父级的属性,而只有类本身的属性,但是既然你把它标记出来了,我会更多地研究它
您应该保留一份副本,而不是原来的清单。
【参考方案1】:
您需要将基类的_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
[]
【讨论】:
以上是关于具有自定义元类的类的所有子类共享相同的属性,即使它们不应该的主要内容,如果未能解决你的问题,请参考以下文章