<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>
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:
<font color='red'>test</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:
<font color='red'
saveData=<function saveData at ...>>test</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>