Python 自定义Dict在施放时放置键
我有一个从dict继承的自定义类(如下所示),当我将对象强制转换为dict时,它会删除名为“type”的键。发生这种情况有什么原因吗 我预期会有以下结果: dm=参数() 打印(dict(dm)) {'tags':'bar','type':'Item','title':'foo'} 我最终得到的是: {'tags':'bar','title':'foo'}Python 自定义Dict在施放时放置键,python,python-3.x,class,dictionary,Python,Python 3.x,Class,Dictionary,我有一个从dict继承的自定义类(如下所示),当我将对象强制转换为dict时,它会删除名为“type”的键。发生这种情况有什么原因吗 我预期会有以下结果: dm=参数() 打印(dict(dm)) {'tags':'bar','type':'Item','title':'foo'} 我最终得到的是: {'tags':'bar','title':'foo'} self.\u映射包含“type”键/值对,因此我不知道可能是什么问题。有人有什么建议吗 class ItemParameter(dict)
self.\u映射包含“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()
这里有两个完全不同的dict
s;隐式地属于您自己对象结构的一部分(因为您继承自dict
)和\u map
。您看到的两个键是通过调用超类\uuuu init\uuuu
(初始值设定项中的第一行)来设置的,该超类委托给update
(您也没有覆盖它),而用于打印的dict
构造函数忽略它们
一般来说,在更改有意义的行为时尝试从dict
本身继承是一个坏主意,因为很容易错过重要的代码路径而无法获得预期的结果。例如,在本例中,我认为处理代码正在调用类的keys
方法,您没有覆盖它,因此它从超类实现中获取keys
,而超类实现绕过了自定义代码
即使您确实重写了它,代码中偶尔也会出现一些快速路径,这些路径最终会绕过自定义代码以提高性能,直接读取子类的dict
组件(根本看不到\u map
或自定义\ugs]etitem
重写)
我建议您考虑从collections.abc.MutableMapping
继承,这样您就不是真正的dict
,但更直观的是,哪些方法需要重写以及必须如何重写
如果您必须是dict
子类,请直接对自己进行操作,而不是\u map
成员,因此您首先可以作为dict
使用。使用super()
调用父方法,例如替换:
def __setitem__(self, k, v):
if k in self._allowed_keys:
self._map[self._allowed_keys[k]] = v
与:
这将改变继承自的dict
结构,而不是碰巧也是dict
的不相关属性。正如ShadowRanger提到的,在映射属性中复制dict
数据是一种糟糕的设计,通常不子类化内置类型会更好
我已经将您的代码减少到复制此“错误”的最低限度,我仍然不完全确定它的行为为何与此完全相同,但我们可以通过确保\uu setitem\uuuuuuuuuuu
将新项目添加到self
以及self.\u 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)
这是一个新版本的你的类,做什么我认为你想要的。它使用Python3版本的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)
输出
title
tags
type
{'title': 'foo', 'tags': 'bar', 'type': 'Item'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item', 'typeKeywords': 'some thing'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item', 'typeKeywords': 'some thing'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item', 'typeKeywords': 'some thing', 'metadata': 3}
我同意你所说的\u map
属性,并使用集合
而不是子类化dict
。但是,我不认为“我相信处理代码正在调用类的keys方法,您没有覆盖它,因此它从超类实现中获取密钥,而超类实现绕过了您的自定义代码”是正确的。如果我们添加:def keys(self):return self.\u map.keys()
,仍然会出现此“错误”。但是,如果我们将ItemParameter
实例转换为列表或元组(即使没有新的keys
方法),则不会发生这种情况,因此我不知道实际原因是什么。此外,如果我们打印\u map
属性,它就具有所有预期的项。但是我刚刚注意到调用.copy()
会返回简化的字典,如果我添加def copy(self):返回self,我们仍然会得到原始错误。\u map.copy()
@shadowranger谢谢您提供的信息。我想确保只有某些键被放入字典,而在cast中,我只希望_allowed_键被放入字典,这是我拥有self的理由。_map
感谢您的回答!
title
tags
type
{'title': 'foo', 'tags': 'bar', 'type': 'Item'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item', 'typeKeywords': 'some thing'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item', 'typeKeywords': 'some thing'}
{'title': 'foo', 'tags': 'bar', 'type': 'Item', 'typeKeywords': 'some thing', 'metadata': 3}