Package Ganga :: Package Utility :: Package external :: Module ordereddict
[hide private]
[frames] | no frames]

Source Code for Module Ganga.Utility.external.ordereddict

  1  # 05-07-03 
  2  #v1.0.3 
  3   
  4  # ordereddict.py 
  5  # A class that is a drop in replacement for an ordinary dictionary. 
  6  # methods that would normally return values/items in a random manner return them 
  7  # in an ordered and consistent manner. 
  8   
  9  # Copyright Michael Foord 
 10  # Not for use in commercial projects without permission. (Although permission will probably be given). 
 11  # If you use this code in a project then please credit me and include a link back. 
 12  # If you release the project then let me know (and include this message with my code !) 
 13   
 14  # No warranty express or implied for the accuracy, fitness to purpose or otherwise for this code.... 
 15  # Use at your own risk !!! 
 16   
 17  # E-mail or michael AT foord DOT me DOT uk 
 18  # Maintained at www.voidspace.org.uk/atlantibots/pythonutils.html 
 19   
 20  import sys          # purely used for the version_info  
 21   
 22  #################################################################################### 
 23   
 24   
25 -class dIter:
26 """Implements a basic dictionary iterator with 3 modes. 27 If mode=0 (default) returns the keys (by returns I mean iterates over !) 28 If mode=1 returns the values 29 If mode=-1 returns the items - (key, value) tuple 30 mode=0 equates to the iterkeys method or __iter__ (for entry in dict) 31 mode=1 equates to the itervalues method. 32 mode=-1 equates to the iteritems method. 33 """
34 - def __init__(self, indict, mode=0):
35 self.thedict = indict 36 self.inseq = indict.keys() 37 self.index = 0 38 self.mode = mode
39
40 - def next(self):
41 if self.index >= len(self.inseq): raise StopIteration 42 thekey = self.inseq[self.index] 43 self.index += 1 44 if not self.mode: 45 return thekey 46 elif self.mode == 1: 47 return self.thedict[thekey] 48 elif self.mode == -1: 49 return (thekey, self.thedict[thekey])
50
51 - def __iter__(self):
52 return self
53 54 #################################################################################### 55
56 -class oDict:
57 """An ordered dictionary. ordereddict = oDict(indict, order)""" 58 __doc__ = """ordereddict = oDict({'a' : 1, 'b' : 2}, True) 59 The dictionary can be initialised with an optional dictionary passed in as the first argument, 60 You can also pass in an order parameter which chooses the sort method. 61 order=True (default) means all ordered methods use the normal sort function. 62 order=False means all ordered methods use the reverse sort function. 63 order=None means no sort function. 64 65 keys, items, iter and pop methods are ordered - based on the key. 66 The ordering is implemented in the keys() function. 67 The iterators are returned using the custom iterator dIter (which will work in three different ways).""" 68
69 - def __init__(self, indict={}, order=True):
70 self._thedict = {} 71 self._thedict.update(indict) 72 self._order = order
73
74 - def __setitem__(self, item, value):
75 """Setting a keyword""" 76 self._thedict[item] = value
77
78 - def __getitem__(self, item):
79 """Fetching a value.""" 80 return self._thedict[item]
81
82 - def __delitem__(self, item):
83 """Deleting a keyword""" 84 del self._thedict[item]
85
86 - def pop(self, item=[], default=None):
87 """Emulates the pop method. 88 If item is not supplied it pops the first value in the dictionary. 89 This is different from the normal dict pop method.""" 90 if item != []: 91 return self._thedict.pop(item, default) 92 else: 93 try: 94 return self._thedict.pop(self.keys()[0]) 95 except IndexError: 96 raise KeyError(': \'pop(): dictionary is empty\'')
97
98 - def popitem(self):
99 """Emulates the popitem method - pops the first one in the list based on the chosen sort method.""" 100 try: 101 theitem = self.keys()[0] 102 except IndexError: 103 raise KeyError(': \'popitem(): dictionary is empty\'') 104 return (theitem, self._thedict.pop(theitem))
105
106 - def has_key(self, item):
107 """Does the dictionary have this key.""" 108 return self._thedict.has_key(item) # does the key exist
109
110 - def __contains__(self, item):
111 """Does the dictionary have this key.""" 112 return self._thedict.has_key(item) # does the key exist
113
114 - def setdefault(self, item, default=None):
115 """Fetch an item if it exists, otherwise set the item to default and return default.""" 116 return self._thedict.setdefault(item, default)
117
118 - def get(self, item, default=None):
119 """Fetch the item if it exists, otherwise return default.""" 120 return self._thedict.get(item, default)
121
122 - def update(self, indict):
123 """Update the current oDdict with the dictionary supplied.""" 124 self._thedict.update(indict)
125
126 - def copy(self):
127 """Create a new oDict object that is a copy of this one.""" 128 return oDict(self._thedict)
129
130 - def dict(self):
131 """Create a dictionary version of this oDict.""" 132 return dict.copy(self._thedict)
133
134 - def clear(self):
135 """Clear oDict.""" 136 self._thedict.clear()
137
138 - def __repr__(self):
139 """An oDict version of __repr__ """ 140 return 'oDict(' + self._thedict.__repr__() + ')'
141
142 - def keys(self):
143 """Return an ordered list of the keys of this oDict.""" 144 thelist = self._thedict.keys() 145 if self._order == True: 146 thelist.sort() 147 elif self._order == False: 148 thelist.sort() 149 thelist.reverse() 150 return thelist
151
152 - def items(self):
153 """Like keys() but returns a list of (key, value)""" 154 return [(key, self._thedict[key]) for key in self.keys()]
155
156 - def values(self):
157 """Like keys() but returns an ordered list of values (ordered by key)""" 158 return [self._thedict[key] for key in self.keys()]
159
160 - def fromkeys(cls, *args):
161 """Return a new oDict initialised from the values supplied. 162 If sys.version_info > 2.2 this becomes a classmethod.""" 163 return oDict(*args)
164 165 if (sys.version_info[0] + sys.version_info[1]/10.0) >= 2.2: 166 fromkeys = classmethod(fromkeys) 167
168 - def __len__(self):
169 return len(self._thedict)
170
171 - def __cmp__(self, other):
172 if hasattr(other, '_thedict'): 173 other = other._thedict 174 return cmp(self._thedict, other)
175
176 - def __eq__(self, other):
177 if hasattr(other, '_thedict'): 178 other = other._thedict 179 return self._thedict.__eq__(other)
180
181 - def __ne__(self, other):
182 if hasattr(other, '_thedict'): 183 other = other._thedict 184 return self._thedict.__ne__(other)
185
186 - def __gt__(self, other):
187 if hasattr(other, '_thedict'): 188 other = other._thedict 189 return self._thedict.__gt__(other)
190
191 - def __ge__(self, other):
192 if hasattr(other, '_thedict'): 193 other = other._thedict 194 return self._thedict.__ge__(other)
195
196 - def __lt__(self, other):
197 if hasattr(other, '_thedict'): 198 other = other._thedict 199 return self._thedict.__lt__(other)
200
201 - def __le__(self, other):
202 if hasattr(other, '_thedict'): 203 other = other._thedict 204 return self._thedict.__le__(other) 205
206 - def __hash__(self):
207 """This just raises a TypeError.""" 208 self._thedict.__hash__()
209
210 - def __iter__(self):
211 """Return an ordered iterator for the oDict.""" 212 return dIter(self)
213
214 - def iteritems(self):
215 """Return an ordered iterator over the the oDict - returning (key, value) tuples.""" 216 return dIter(self, -1)
217
218 - def iterkeys(self):
219 """Return an ordered iterator over the keys the oDict.""" 220 return dIter(self)
221
222 - def itervalues(self):
223 """Return an ordered iterator over the the values of the oDict - ordered by key.""" 224 return dIter(self, 1)
225
226 - def __str__(self):
227 """An oDict version of __str__ """ 228 return 'oDict(' + self._thedict.__str__() + ')'
229 230 ############################################################################################ 231 232 if __name__ == '__main__': 233 dictmethods = ['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__str__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] 234 odict = oDict({'x' : 'a', 'y' : 'b', 'z' : 'c'}) 235 print 'print oDict.__doc__ \n', oDict.__doc__ 236 print 237 print 238 print 'Attribute Test.\nTesting against the full attribute list for a normal dictionary. (40 attributes)' 239 for entry in dictmethods: 240 if not hasattr(odict, entry): 241 print 'oDict doesn\'t have attribute \'%s\'' % entry 242 print 'See the docs as to why those attributes are missing !!' 243 print 'Method test.\nIf nothing prints below this then all the tests passed !\n' 244 dlist = [] 245 for key in odict.iterkeys(): dlist.append(key) 246 if dlist != ['x', 'y', 'z']: print 'Order fail in iterkeys method.' 247 248 dlist = [] 249 for value in odict.itervalues(): dlist.append(value) 250 if dlist != ['a', 'b', 'c']: print 'Order fail in itervalues method.' 251 252 dlist = [] 253 for item in odict.iteritems(): dlist.append(item) 254 if dlist != [('x','a'), ('y','b'), ('z','c')]: print 'Order fail in iteritems method.' 255 256 if not odict.keys() == ['x', 'y', 'z']: print 'Order fail in keys method.' 257 if not odict.values() == ['a', 'b', 'c']: print 'Order fail in values method.' 258 if not odict.items() == [('x','a'), ('y','b'), ('z','c')]: print 'Order fail in items method.' 259 dlist = [] 260 while odict: 261 dlist.append(odict.pop()) 262 if len(dlist) > 4: 263 print 'Fail in pop to remove items' 264 break 265 if dlist != ['a', 'b', 'c']: print 'Order fail in pop method.' 266 if not odict.fromkeys({'test':'z', 'fish':4}, False) == oDict({'test':'z', 'fish':4}, False): print 'Odd behaviour in fromkeys method.' 267 268 269 """ 270 oDict is an ordered dictionary. 271 It behaves as a drop in replacement for an ordinary dictionary in almost every circumstance. 272 Many dictionary methods which normally return a random value, or return values in a randomn order. 273 Those methods in oDict return values in an ordered and consistent manner. 274 The ordering is applied in the keys() method and uses the Python sort() method of lists to do the sorting. 275 You can additionally set it to apply the reverse method by passing in a parameter when you create the instance. 276 See the oDict docstring for more details. 277 278 An ordered dictinary is useful where, for example, a consistent return order for the iterators and pop methods is helpful. 279 I use it in FSDM markup structures (describing files and directories in a file structure) so that the markup files are built in a consistent order. 280 281 Methods which are now ordered are : 282 283 pop, popitem, keys, items, values 284 iteritems, iterkeys, itervalues, __iter__ ( for key in odict ) 285 286 287 As oDict has methods defined for almost all the dictionary methods, and also has custom iterators, 288 it would be a good template for anyone else who wanted to create a new dictionary type with custom access methods etc. 289 290 Doesn't subclass dict or use the iter function, so I think might be compatible with versions of Python pre 2.2 ? 291 292 Extra Methods, Not in a Normal dictionary : 293 'dict' 294 295 'pop' is slightly different to the normal dictionary method, it can be used without a parameter. 296 'str' and '__repr__' are modified to indicate this is an oDict rather than just a dictionary. 297 298 A lot of the methods that would return new dictionaries (copy, fromkeys) return new oDicts (hence the new dict method which returns an ordinary dictionary copy of the oDIct) 299 300 'Not Implemented Yet' Methods Include : 301 '__class__' : The default is fine. 302 '__getattribute__', '__setattr__', '__delattr__' : What the heck are these used for in a dict, probably raise errors. The standard 'classic' methods will be fine for us 303 '__new__' : not a new style class so don't need it 304 '__reduce__', '__reduce_ex__', : To do with pickling, I don't understand, hopefully Python can do something sensible anyway 305 306 The only time oDict won't act as a replacement for a dict is if the isinstance test is used. 307 In Python 2.2 you can fix this by making oDict a subclass of dict. 308 309 We could make oDict a bit more lightweight by overloading getattr. 310 Several methods that just call the same method on the underlying _thedict could all be replaced by one method that called getattr. 311 As one of the aims of oDict was to create a full dictionary like object, with all the methods defined, I won't do this. (__getitem__ and __setitem__ are two methods we could do away with). 312 313 314 TODO/ISSUES 315 316 317 CHANGELOG 318 319 05-07-04 Version 1.0.3 320 Fixed a bug in get. 321 Use some slightly more pythonic hasattr tests rather than isinstance. 322 Got rid of the slightly odd dum class. 323 Got rid of my suggested, nonsensical, __class__ stuff. 324 Changed the tests to use keys that would naturally be unordered ! 325 326 20-06-04 Version 1.0.2 327 Slight change to the dum class, to give it a __cmp__ method. 328 329 17-06-04 Version 1.0.1 330 clear method is slightly better. (clears dictionary rather than rebinds) 331 Sorted out a few index errors where empty dictionaries are used. (raise KeyError rather than IndexError) 332 Made fromkeys a classmethod where Python Version > 2.2 333 334 16-06-04 Version 1.0.0 335 First version, appears to work fine. 336 337 """ 338