Welcome, guest | Sign In | My Account | Store | Cart

<code>A dict extension that has the following features:

1. dual data storages: use 'bd.x' or 'bd["x"]' for one,
   and bd.getDict() for another.

2. All the followings are equivalent::

       bd['x']= val
       bd.x   = val
       bd.setItem('x', val)  # Return bd

3. bd.setDict('x',val) will save 'x' to bd.__dict__,
   but not bd.items

4. When copy, copy the internal object correctly.

</code>

Python, 175 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import cPickle

class BaseDict(dict):
    '''
        A dict allows inputting data as adict.xxx as well as adict['xxx']

        In python obj:
        
            obj.var=x  ---> 'var' be in obj.__dict__ (this is python default)

        In python dict:
        
            dict['var']=x ---> 'var' be in dict.items(this is dict behavior)

        In BaseDict:  

            let bd=BaseDict()
            
            Both bd.var and bd['var'] will save x to bd.items 
            and bd.setDict('var', x) will save x to bd.__dict__ 

            This allows an easier access of the variables.        
  
    '''
    def __init__(self, data=None):
        if data:  dict.__init__(self, data)
        else:     dict.__init__(self)
        dic = self.__dict__
        dic['__ver__']   ='20041208_1'
        dic['__author__']='Runsun Pan'
    
    def __setattr__(self, name, val):
        if name in self.__dict__:  self.__dict__[name]= val        
        else:   self[name] = val
        
    def __getattr__(self, name):
        if name in self.__dict__:  return self.__dict__[name]        
        else:  return self[name] 
           
    def setDict(self, name, val): 
        '''
            setDict(name, val): Assign *val* to the key *name* of __dict__.
         
            :Usage:
            
            >>> bd = BaseDict()
            >>> bd.getDict()['height']   
            Traceback (most recent call last):
            ...
            KeyError: 'height'
            >>> bd.setDict('height', 160)  # setDict 
            {}
            >>> bd.getDict()['height']
            160

            '''
        self.__dict__[name] = val
        return self 

    def getDict(self): 
        ''' 
            Return the internal __dict__.
            
            :Usage:
            
            >>> bd = BaseDict()
            >>> bd.getDict()['height']
            Traceback (most recent call last):
            ...
            KeyError: 'height'
            >>> bd.setDict('height', 160)
            {}
            >>> bd.getDict()['height']
            160
            '''
        return self.__dict__
        
    def setItem(self, name, val): 
        ''' 
            Set the value of dict key *name* to *val*. Note this dict 
            is not the __dict__.

            :Usage:
            
            >>> bd = BaseDict()
            >>> bd
            {}
            >>> bd.setItem('sex', 'male')
            {'sex': 'male'}
            >>> bd['sex'] = 'female'
            >>> bd
            {'sex': 'female'}
            '''
        self[name] = val
        return self
    
    def __getstate__(self): 
        ''' Needed for cPickle in .copy() '''
        return self.__dict__.copy() 

    def __setstate__(self,dict): 
        ''' Needed for cPickle in .copy() '''
        self.__dict__.update(dict)   

    def copy(self):   
        ''' 
            Return a copy. 
            
            :Usage:
            
            >>> bd = BaseDict()
            >>> bd['name']=[1,2,3]
            >>> bd
            {'name': [1, 2, 3]}
            >>> bd2 = bd.copy()
            >>> bd2
            {'name': [1, 2, 3]}
            >>> bd == bd2
            True
            >>> bd is bd2
            False
            >>> bd['name']==bd2['name']
            True
            >>> bd['name'] is bd2['name']
            False
            >>> bd2['name'][0]='aa'
            >>> bd2['height']=60
            >>> bd
            {'name': [1, 2, 3]}
            >>> bd2
            {'name': ['aa', 2, 3], 'height': 60}
                
            '''
        return cPickle.loads(cPickle.dumps(self))

   

bd = BaseDict()

# The following 3 steps are equivilent: 
bd.setItem('sex', 'male')
print bd                   #{'sex': 'male'}
bd['sex'] = 'female'
print bd                   #{'sex': 'female'}
bd.sex = 'huh?'
print bd                   #{'sex': 'huh?'}

# This is the __dict__
bd.setDict('height', 60)   
print bd.getDict()['height']  # 60
print bd.getDict()
# {'__ver__': '20041208_1', 'height': 60, '__author__': 'Runsun Pan'}

# Check copy():
bd['name']=[1,2,3]
print bd           # {'name': [1, 2, 3], 'sex': 'huh?'}
bd2 = bd.copy()
print bd2          # {'name': [1, 2, 3], 'sex': 'huh?'}
print bd == bd2                  #True
print bd is bd2                  #False
print bd['name']==bd2['name']    #True
print bd['name'] is bd2['name']  #False
bd2['name'][0]='aa'
print bd.name                    #[1, 2, 3] 
print bd2.name                   #['aa', 2, 3]


bd2['height']=100
print bd   # {'name': [1, 2, 3], 'sex': 'huh?'}
print bd.getDict()
           # {'__ver__': '20041208_1', 'height': 60, '__author__': 'Runsun Pan'}
            
print bd2  # {'height': 100, 'name': ['aa', 2, 3], 'sex': 'huh?'}
print bd2.getDict()
           # {'__ver__': '20041208_1', '__author__': 'Runsun Pan', 'height': 60}
           

<pre> Background:

The idea of having a dual storage dict came from an attempt to design a html element object. I want to have an object that represents some tag like:

&lt;font color='red'>test&lt;/font>

A font object should be able to do this:

font.color = 'red'

However, in python default behavior, this 'color' will be put into __dict__, as well as any other customized variables like 'getOrder', 'saveData' ... And we don't really want the tag looks like this:

&lt;font color='red'
 saveData=&lt;function saveData at ...>>test&lt;/font>

Thus, a dual-storage dict came up, one storage for html tag attribute (color, size .. for , etc), and the other is the intrinsic __dict__, for the common python attribute access.

There might be some other uses of this class.

Related:

'Keith Dart' : 'http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473786', 'Jimmy Retzlaff': 'http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/361668'

The above 2 recipes also deal with "attribute-like" accessing of dict items (but not the copy part; see below).

Copy:

The standard python dict (as well as the classes linked above) doesn't deal with copy successfully:

>>> a={'aa':[1,2,3]}
>>> b=a.copy()
>>> b
{'aa': [1, 2, 3]}
>>> b['aa'][0]=9
>>> b
{'aa': [9, 2, 3]}
>>> a
{'aa': [9, 2, 3]}  # we dont want this

By serializing the class and un-serializing it back, BaseDict handles the copy nicely. Note the code in __getstate__ and __setstate__, which are needed for this deepcopy to work. </pre>

Created by runsun pan on Thu, 26 Jan 2006 (PSF)
Python recipes (4591)
runsun pan's recipes (5)

Required Modules

Other Information and Tasks