Skip to content Skip to sidebar Skip to footer

How To Define A Super Powerful Class-style Dict Object?

Assume that the object meeting my need is called classdic, then the functions of an instance of class classdic are: Query, update, add, and delete data can be all realized in clas

Solution 1:

Subclass 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

This will not automatically create nested instances of itself; you'll need to loop over the input dictionary and create nested objects yourself.

But it does offer attribute access and default values:

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

To build your tree from an existing dictionary, use the from_dictionaries() class method:

>>> 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

Note that keys on the dictionary can mask methods; keep that in mind when inserting keys that match dictionary methods:

>>> 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

Solution 2:

If you want to be able to do

cdic.two = 2 

You should also override __setattr__

def __setattr__(self, name, val):
    self[name] = val

Solution 3:

Here's a variation of Martijn Pieters' answer which is a variation of this answer (which itself is a variation of this, which is based on a blog entry by James Robert). However, unlike the Martijn's it will automatically create nested instances of itself. A custom__repr__()has also been added.

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

Solution 4:

After studying all the answers,I wrote the solution by myself.

It meets my needs well and no "import xxx" is required,also kind to print.

(I think mine is better than this one I have planned to share yesterday)

my solution:

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 result:

-------------------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}}}

Post a Comment for "How To Define A Super Powerful Class-style Dict Object?"