演员时自定义Dict删除键

时间:2017-08-08 10:45:54

标签: python python-3.x class dictionary

我有一个从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()

2 个答案:

答案 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的无关属性。