我有一个从dict继承的自定义类(如下所示),当我将对象转换为dict时,它会删除名为'type'的键。有什么理由发生这种情况吗?
我期待以下结果:
dm =参数()
打印(字典(DM))
{'tags':'bar','type':'Item','title':'foo'}
我最终获得的是:
{'tags':'bar','title':'foo'}
self._map
包含'type'键/值对,所以我不知道可能是什么问题。有没有人有任何建议?
class ItemParameter(dict):
"""
"""
def __init__(self,
title,
tags):
super(ItemParameter, self).__init__(title=title, tags=tags)
self._map = {}
self._allowed_keys = {'type':'type',
'title': 'title',
'tags':'tags',
'thumbnail': 'thumbnail',
'thumbnailurl': 'thumbnailurl',
'metadata': 'metadata',
'type_keywords' : 'typeKeywords',
'description': 'description',
'snippet' : 'snippet'}
self['type'] = "Item"
self['title'] = title
self['tags'] = tags
def __iter__(self):
for k, v in self.items():
yield k, v
def __setitem__(self, k, v):
if k in self._allowed_keys:
self._map[self._allowed_keys[k]] = v
def __getitem__(self, k):
if k in self._map:
return self._map[k]
elif k in self._allowed_keys and \
self._allowed_keys[k] in self._map:
return self._map[self._allowed_keys[k]]
def __setattr__(self, k, v):
if k in {'_map','_dynamic', '_allowed_keys',
'_ipython_canary_method_should_not_exist_'}:
super(ItemParameter, self).__setattr__(k,v)
else:
if k in self._allowed_keys.keys():
self._map[self._allowed_keys[k]] = v
def __getattr__(self, k):
if k == {'_map','_dynamic', '_allowed_keys',
'_ipython_canary_method_should_not_exist_'}:
super(ItemParameter, self).__getattr__(k)
else:
if k in self._allowed_keys.keys() and \
self._allowed_keys[k] in self._map:
return self._map[self._allowed_keys[k]]
def __delattr__(self, key):
return self._map.__delitem__(key)
def __contains__(self, k):
return self._map.__contains__(k)
def __repr__(self):
return str(self._map)
def __str__(self):
return self.__repr__()
def items(self):
return self._map.items()
答案 0 :(得分:2)
正如ShadowRanger所提到的那样,在dict
属性中复制._map
数据是不好的设计,而且通常情况下,如果不对内置类型进行子类化则要好得多。
我已经将代码缩减到了最低限度,再现了这个“错误”,我仍然不能完全确定为什么它的行为完全就像它一样,但我们可以修复bug确保__setitem__
将新项目添加到self
以及self._map
。例如:
def __setitem__(self, k, v):
if k in self._allowed_keys:
k = self._allowed_keys[k]
self._map[k] = v
super(ItemParameter, self).__setitem__(k, v)
这是你班级的新版本,可以完成我想你想要的。它使用{3}的Python 3版本,其形式为零。
super()
<强>输出强>
class ItemParameter(dict):
allowed = {
'type':'type',
'title': 'title',
'tags':'tags',
'thumbnail': 'thumbnail',
'thumbnailurl': 'thumbnailurl',
'metadata': 'metadata',
'type_keywords' : 'typeKeywords',
'description': 'description',
'snippet' : 'snippet',
}
def __init__(self, title, tags):
super().__init__(title=title, tags=tags)
self['type'] = "Item"
def __setitem__(self, k, v):
if k in ItemParameter.allowed:
k = ItemParameter.allowed[k]
super().__setitem__(k, v)
def update(self, other):
for k, v in other.items():
self[k] = v
# test
p = ItemParameter(title='foo', tags='bar')
for t in p:
print(t)
print(p)
print(p.copy())
print(dict(p))
p['type_keywords'] = 'some thing'
print(p)
p['bad'] = 'other thing'
print(p)
p.update({'a':1, 'b':2, 'metadata': 3})
print(p)
答案 1 :(得分:1)
你在这里有两个完全不同的dict
;那个隐含在你自己的对象结构中的部分(因为你继承自dict
)和_map
。您看到的两个键是您通过调用超级类__init__
(初始值设定项中的第一行)设置的键,它们委托给update
(您也没有覆盖),而{dict
你用print
的构造函数忽略了它们。
一般来说,尝试在改变有意义的行为时继承dict
本身是一个坏主意,因为很容易错过重要的代码路径而不能获得预期的结果。例如,在这种情况下,我相信处理代码正在调用您没有覆盖的类的keys
方法,因此它从超类实现中获取keys
,从而绕过您的自定义代码。 / p>
即使您确实覆盖它,代码周围也会偶尔出现快速路径,最终绕过自定义代码以提升性能,直接读取子类的dict
组件(不看_map
或者您的自定义__[gs]etitem__
会覆盖。
我建议您继续使用collections.abc.MutableMapping
继承,因此您不是真正的dict
,但更直观的是哪些方法需要重写以及您必须如何执行。
如果您必须是dict
子类,请直接对自己进行操作,而不是_map
成员,这样您就可以首先使用dict
。使用super()
调用父方法,例如替换:
def __setitem__(self, k, v):
if k in self._allowed_keys:
self._map[self._allowed_keys[k]] = v
使用:
def __setitem__(self, k, v):
if k in self._allowed_keys:
super().__setitem__(self._allowed_keys[k], v)
将改变您继承的dict
结构,而不是恰好是dict
的无关属性。