1

假设调用了满足我需求的对象classdic,那么类实例的功能classdic是:

  1. 查询、更新、添加、删除数据都可以通过class方式和dict方式(称为“两种方式”)来实现。

  2. 当某个属性或键不存在时,classdic 的实例可以自动构建它并使其等于实例和字典上的默认值,因此我们可以双向查询(注意:不是添加,只是查询)。

那么,我该如何实现这个类呢?

下面的示例显示了此类的实例如何工作:

dic={'one':1,
'two':{'four':4,'five':{'six':6,'seven':7}},
'three':3}

cdic=classdic(dic,default=100)

-------------------query in two ways-------------------------------------------
>>> cdic.one
1
>>> cdic.two
{'four':4,'five':{'six':6,'seven':7}}
>>> cdic.two.five.six
6
>>> cdic['two']['five']['six']
6
-------------------update in two ways-------------------------------------------
>>> cdic['two']['five']['six']=7
>>> cdic.two.five.six
7
>>> cdic.two.five.six=8
>>> cdic['two']['five']['six']
8
-------------------add in two ways-------------------------------------------
>>> cdic['two']['five']['eight']=8
>>> cdic.two.five.eight
8
>>> cdic.two.five.nine=9
>>> cdic['two']['five']['nine']
9
-------------------query default in two ways-------------------------------------------
>>> print cdic['ten']
100
>>> cdic.ten
100
>>> print cdic.eleven
100
>>> cdic['eleven']
100
-------------------the final state of cdic-------------------------------------------
>>> cdic
{'eleven': 100, 'three': 3, 'two': {'four': 4, 'five': {'nine': 9, 'seven': 7, 'six': 8, 'eight': 8}}, 'ten': 100, 'one': 1}
4

4 回答 4

4

子类collections.defaultdict()

from collections import defaultdict, Mapping

class default_attribute_dict(defaultdict):
    def __init__(self, *args, **kwargs):
        super(default_attribute_dict, self).__init__(*args, **kwargs)
        self.__dict__ = self

    def __getattr__(self, name):
        # trigger default
        return self[name]

    @classmethod
    def from_dictionaries(cls, d, default=lambda: None):
        cdic = cls(default)
        for key, value in d.iteritems():
            if isinstance(value, Mapping):
               value = cls.from_dictionaries(value, default=default)
            cdic[key] = value
        return cdic

不会自动创建自身的嵌套实例;您需要遍历输入字典并自己创建嵌套对象。

但它确实提供了属性访问和默认值:

>>> cdic = default_attribute_dict(lambda: 100)
>>> cdic.hundred
100
>>> cdic['ten'] = 10
>>> cdic.ten
10
>>> cdic['ten']
10

要从现有字典构建树,请使用from_dictionaries()类方法:

>>> cdic = default_attribute_dict.from_dictionaries(dic, default=lambda: 100)
>>> cdic
defaultdict(<function <lambda> at 0x109998848>, {'one': 1, 'three': 3, 'two': defaultdict(<function <lambda> at 0x109998848>, {'four': 4, 'five': defaultdict(<function <lambda> at 0x109998848>, {'seven': 7, 'six': 6})})})
>>> cdic.two.four
4

请注意,字典上的键可以屏蔽方法;在插入与字典方法匹配的键时请记住这一点:

>>> cdic = default_attribute_dict.from_dictionaries(dic, default=lambda: 100)
>>> cdic.keys
<built-in method keys of default_attribute_dict object at 0x7fdd0bcc9ac0>
>>> cdic['keys']
100
>>> cdic.keys
100
>>> cdic.keys()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
于 2013-10-07T10:08:22.970 回答
0

如果你想能够做到

cdic.two = 2 

您还应该覆盖__ setattr __

def __setattr__(self, name, val):
    self[name] = val
于 2013-10-07T10:17:57.260 回答
0

这是 Martijn Pieters答案的变体,它是这个答案的变体(它本身就是this的变体,它基于James Robert的博客条目)。然而,与 Martijn 不同的是,它自动创建自身的嵌套实例。__repr__()还添加了一个自定义。

from collections import defaultdict, Mapping

class classdict(defaultdict):
    def __init__(self, *args, **kwargs):
        super(classdict, self).__init__(*args, **kwargs)
        self.__dict__ = self

    def __getattr__(self, name):
        return self[name]  # trigger default

    def __missing__(self, key):
        default = (None if self.default_factory is None else
                   self.default_factory())
        self[key] = classdict.from_dict({key: default},
                                        default=self.default_factory)
        return self[key]

    def __repr__(self):
        return '{}({}, default={})'.format(self.__class__.__name__,
                                           dict(self.__dict__),  # no recursion
                                           self.default_factory)
    @classmethod
    def from_dict(cls, d, default=lambda: None):
        cdic = cls(default)
        for key, value in d.iteritems():
            cdic[key] = (value if not isinstance(value, Mapping)
                         else cls.from_dict(value, default=default))
        return cdic

if __name__ == '__main__':

    dic={'one': 1,
         'two': {'four': 4,
                 'five': {'six': 6,
                          'seven': 7}
                },
         'three': 3
        }

    cdic = classdict.from_dict(dic, default=lambda: 100)

    print 'cdic.one:', cdic.one
    print 'cdic.two:', cdic.two
    print 'cdic.two.five.six:', cdic.two.five.six
    print "cdic['two']['five']['six']:", cdic['two']['five']['six']
    print "cdic['two']['five']['six'] = 7"
    cdic['two']['five']['six'] = 7
    print 'cdic.two.five.six:', cdic.two.five.six
    print 'cdic.two.five.six = 8'
    cdic.two.five.six = 8
    print "cdic['two']['five']['six']:", cdic['two']['five']['six']
    print "cdic['two']['five']['eight'] = 8"
    cdic['two']['five']['eight'] = 8
    print 'cdic.two.five.eight:', cdic.two.five.eight
    print 'cdic.two.five.nine = 9'
    cdic.two.five.nine = 9
    print "cdic['two']['five']['nine']:", cdic['two']['five']['nine']
    print "cdic['ten']:", cdic['ten']
    print 'cdic.ten:', cdic.ten
    print 'cdic.eleven:', cdic.eleven
    print "cdic['eleven']:", cdic['eleven']
    print "final cdic:\n    ", cdic
于 2013-10-07T15:45:49.283 回答
0

在研究了所有答案后,我自己写了解决方案。

它很好地满足了我的需求,不需要“import xxx”,也可以打印。

(我认为我的比我昨天计划分享的这个更好)

我的解决方案:

class DictObj(dict):
    default=None
    def __init__(self, dic, default):
        DictObj.default = default
        for key,value in dic.items():
            if isinstance(value,dict):
                self.__setattr__(key, DictObj(value,DictObj.default))
            else:
                self.__setattr__(key, value)  
    def __getitem__(self, key):
        return self.__getattr__(key )

    def __setitem__(self, key, value):
        self.__setattr__(key,value)

    def __getattr__( self ,key ):
        if key not in self:
            self.__setattr__(key,DictObj.default)
        return self.__dict__[key] 

    def __setattr__( self ,key ,value ):
        self.__dict__[key]=value
        dict.__setitem__(self, key, value)

    def printself(self):
        print self     

dic={'one':1,
     'two':{
         'four':4,
         'five':{
             'six':6,
             'seven':7,}},
     'three':3}

cdic=DictObj(dic,100)

print '-------------------the start state of cdic-------------------------------------------'
print cdic

print '-------------------query in two ways-------------------------------------------'
print 'cdic.two.five-->',cdic.two.five
print "cdic['two']['five']-->",cdic['two']['five']
print 'cdic.two.five.six-->',cdic.two.five.six
print "cdic['two']['five']['six']-->",cdic['two']['five']['six']

print '-------------------update in two ways-------------------------------------------'
cdic['two']['five']['six']=7
print "cdic['two']['five']['six']=7"
print "cdic.two.five.six-->",cdic.two.five.six 

cdic.two.five.six=6
print "cdic.two.five.six=6"
print "cdic['two']['five']['six']-->",cdic['two']['five']['six']

print '-------------------add in two ways-------------------------------------------'

cdic['two']['five']['eight']=8
print "cdic['two']['five']['eight']=8"
print "cdic.two.five.eight-->",cdic.two.five.eight

cdic.two.five.nine=9
print "cdic.two.five.nine=9"
print "cdic['two']['five']['nine']-->",cdic['two']['five']['nine']

print '-------------------query default in two ways-------------------------------------------'
print "cdic['ten']-->",cdic['ten']
print "cdic.eleven-->",cdic.eleven
print "cdic.two.five.twelve-->",cdic.two.five.twelve
print '-------------------the final state of cdic-------------------------------------------'

print cdic,'\n'
cdic.printself()

结果:

-------------------the start state of cdic-------------------------------------------
{'one': 1, 'three': 3, 'two': {'four': 4, 'five': {'seven': 7, 'six': 6}}}
-------------------query in two ways-------------------------------------------
cdic.two.five--> {'seven': 7, 'six': 6}
cdic['two']['five']--> {'seven': 7, 'six': 6}
cdic.two.five.six--> 6
cdic['two']['five']['six']--> 6
-------------------update in two ways-------------------------------------------
cdic['two']['five']['six']=7
cdic.two.five.six--> 7
cdic.two.five.six=6
cdic['two']['five']['six']--> 6
-------------------add in two ways-------------------------------------------
cdic['two']['five']['eight']=8
cdic.two.five.eight--> 8
cdic.two.five.nine=9
cdic['two']['five']['nine']--> 9
-------------------query default in two ways-------------------------------------------
cdic['ten']--> 100
cdic.eleven--> 100
cdic.two.five.twelve--> 100
-------------------the final state of cdic-------------------------------------------
{'eleven': 100, 'one': 1, 'three': 3, 'ten': 100, 'two': {'four': 4, 'five': {'nine': 9, 'seven': 7, 'six': 6, 'eight': 8, 'twelve': 100}}}

{'eleven': 100, 'one': 1, 'three': 3, 'ten': 100, 'two': {'four': 4, 'five': {'nine': 9, 'seven': 7, 'six': 6, 'eight': 8, 'twelve': 100}}}
于 2013-10-08T06:59:28.163 回答