Package Bio :: Package EUtils :: Module MultiDict
[hide private]
[frames] | no frames]

Source Code for Module Bio.EUtils.MultiDict

  1  """Dictionary-like objects which allow multiple keys 
  2   
  3  Python dictionaries map a key to a value.  Duplicate keys are not 
  4  allowed, and new entries replace old ones with the same key.  Order is 
  5  not otherwise preserved, so there's no way to get the items in the 
  6  order they were added to a dictionary. 
  7   
  8  Some types of data is best stored in dictionary-like object which 
  9  allow multiple values per key.  Some of these need the input order 
 10  strongly preserved, so the items can be retrieved in the same order as 
 11  they were added to the dictionary.  That is the OrderedMultiDict. 
 12   
 13  Others need a weaker ordering guarantee where the order of values for 
 14  a given key is preserved but the order between the keys is not.  That 
 15  is UnorderedMultiDict.  (Because strong ordering isn't needed, it's 
 16  faster to delete from an UnorderedMultiDict.) 
 17   
 18  To create a MultiDict, pass in an object which implements the 
 19  'allitems' method and returns a list of (key, value) pairs, or 
 20  pass in the list of (key, value) pairs directly. 
 21   
 22  The two MultiDict classes implement the following dictionary methods 
 23    d["lookup"], 
 24    d["key"] = value 
 25    del d[key] 
 26    d.get("key", default = None) 
 27    d1 == d2, d1 != d2, len(d), iter(d), str(d) 
 28    d.keys(), d.values(), d.items() 
 29   
 30  The new methods are: 
 31    d.getall(key) 
 32    d.allkeys() 
 33    d.allvalues() 
 34    d.allitems() 
 35     
 36    >>> import MultiDict 
 37    >>> od = MultiDict.OrderedMultiDict() 
 38    >>> od["Name"] = "Andrew" 
 39    >>> od["Color"] = "BLUE" 
 40    >>> od["Name"] = "Dalke" 
 41    >>> od["Color"] = "Green" 
 42    >>> od[3] = 9 
 43    >>> len(od) 
 44    3 
 45    >>> od["Name"] 
 46    'Dalke' 
 47    >>> od.getall("Name") 
 48    ['Andrew', 'Dalke'] 
 49    >>> for k, v in od.allitems(): 
 50    ...     print "%r == %r" % (k, v) 
 51    ... 
 52    'Name' == 'Andrew' 
 53    'Color' == 'BLUE' 
 54    'Name' == 'Dalke' 
 55    'Color' == 'Green' 
 56    3 == 9 
 57    >>> del od["Name"] 
 58    >>> len(od) 
 59    2 
 60    >>> for k, v in od.allitems(): 
 61    ...     print "%r == %r" % (k, v) 
 62    ... 
 63    'Color' == 'BLUE' 
 64    'Color' == 'Green' 
 65    3 == 9 
 66    >>> 
 67   
 68  The latest version of this code can be found at 
 69    http://www.dalkescientific.com/Python/ 
 70  """ 
 71  # Written in 2003 by Andrew Dalke, Dalke Scientific Software, LLC. 
 72  # This software has been released to the public domain.  No 
 73  # copyright is asserted. 
 74   
 75  # Implementation inheritence -- not asserting a class hierarchy here 
 76  # 
 77  # If there is a class hierarchy, OrderedMultiDict is a child of 
 78  # UnorderedMultiDict because it makes stronger but not different 
 79  # guarantees on how the data works, at least data-wise. 
 80  # Performance-wise, Ordered has a slower (O(n)) than Unordered (O(1)). 
 81  # Convince me otherwise and I'll change.  Besides, hierarchies are 
 82  # overrated. 
83 -class _BaseMultiDict:
84 - def __str__(self):
85 """shows contents as if this is a dictionary 86 87 If multiple values exist for a given key, use the last 88 one added. 89 """ 90 d = {} 91 for k in self.data: 92 d[k] = self.data[k][-1] 93 return str(d)
94 - def __len__(self):
95 """the number of unique keys""" 96 return len(self.data)
97
98 - def __getitem__(self, key):
99 """value for a given key 100 101 If more than one value exists for the key, use one added most recently 102 """ 103 return self.data[key][-1]
104
105 - def get(self, key, default = None):
106 """value for the given key; default = None if not present 107 108 If more than one value exists for the key, use the one added 109 most recently. 110 """ 111 return self.data.get(key, [default])[-1]
112
113 - def __contains__(self, key):
114 """check if the key exists""" 115 return key in self.data
116
117 - def keys(self):
118 """unordered list of unique keys""" 119 return self.data.keys()
120
121 - def values(self):
122 """unordered list of values 123 124 If more than one value exists for a given key, use the value 125 added most recently. 126 """ 127 return [x[-1] for x in self.data.values()]
128
129 - def items(self):
130 """unordered list of key/value pairs 131 132 If more than one value exists for a given key, use the value 133 added most recently. 134 """ 135 return [(k, v[-1]) for k, v in self.data.items()]
136
137 - def getall(self, key):
138 """Get all values for a given key 139 140 Multiple values are returned in input order. 141 If the key does not exists, returns an empty list. 142 """ 143 return self.data[key]
144
145 - def __iter__(self):
146 """iterate through the list of unique keys""" 147 return iter(self.data)
148 149
150 -class OrderedMultiDict(_BaseMultiDict):
151 """Store key/value mappings. 152 153 Acts like a standard dictionary with the following features: 154 - duplicate keys are allowed; 155 156 - input order is preserved for all key/value pairs. 157 158 >>> od = OrderedMultiDict([("Food", "Spam"), ("Color", "Blue"), 159 ... ("Food", "Eggs"), ("Color", "Green")]) 160 >>> od["Food"] 161 'Eggs' 162 >>> od.getall("Food") 163 ['Spam', 'Eggs'] 164 >>> list(od.allkeys()) 165 ['Food', 'Color', 'Food', 'Color'] 166 >>> 167 168 The order of keys and values(eg, od.allkeys() and od.allitems()) 169 preserves input order. 170 171 Can also pass in an object to the constructor which has an 172 allitems() method that returns a list of key/value pairs. 173 174 """
175 - def __init__(self, multidict = None):
176 self.data = {} 177 self.order_data = [] 178 if multidict is not None: 179 if hasattr(multidict, "allitems"): 180 multidict = multidict.allitems() 181 for k, v in multidict: 182 self[k] = v
183 - def __eq__(self, other):
184 """Does this OrderedMultiDict have the same contents and order as another?""" 185 return self.order_data == other.order_data
186 - def __ne__(self, other):
187 """Does this OrderedMultiDict have different contents or order as another?""" 188 return self.order_data != other.order_data
189
190 - def __repr__(self):
191 return "<OrderedMultiDict %s>" % (self.order_data,)
192
193 - def __setitem__(self, key, value):
194 """Add a new key/value pair 195 196 If the key already exists, replaces the existing value 197 so that d[key] is the new value and not the old one. 198 199 To get all values for a given key, use d.getall(key). 200 """ 201 self.order_data.append((key, value)) 202 self.data.setdefault(key, []).append(value)
203
204 - def __delitem__(self, key):
205 """Remove all values for the given key""" 206 del self.data[key] 207 self.order_data[:] = [x for x in self.order_data if x[0] != key]
208
209 - def allkeys(self):
210 """iterate over all keys in input order""" 211 for x in self.order_data: 212 yield x[0]
213 - def allvalues(self):
214 """iterate over all values in input order""" 215 for x in self.order_data: 216 yield x[1]
217 - def allitems(self):
218 """iterate over all key/value pairs in input order""" 219 return iter(self.order_data)
220 221 222
223 -class UnorderedMultiDict(_BaseMultiDict):
224 """Store key/value mappings. 225 226 Acts like a standard dictionary with the following features: 227 - duplicate keys are allowed; 228 229 - input order is preserved for all values of a given 230 key but not between different keys. 231 232 >>> ud = UnorderedMultiDict([("Food", "Spam"), ("Color", "Blue"), 233 ... ("Food", "Eggs"), ("Color", "Green")]) 234 >>> ud["Food"] 235 'Eggs' 236 >>> ud.getall("Food") 237 ['Spam', 'Eggs'] 238 >>> 239 240 The order of values from a given key (as from ud.getall("Food")) 241 is guaranteed but the order between keys (as from od.allkeys() 242 and od.allitems()) is not. 243 244 Can also pass in an object to the constructor which has an 245 allitems() method that returns a list of key/value pairs. 246 247 """
248 - def __init__(self, multidict = None):
249 self.data = {} 250 if multidict is not None: 251 if hasattr(multidict, "allitems"): 252 multidict = multidict.allitems() 253 for k, v in multidict: 254 self[k] = v
255
256 - def __eq__(self, other):
257 """Does this UnorderedMultiDict have the same keys, with values in the same order, as another?""" 258 return self.data == other.data
259
260 - def __ne__(self, other):
261 """Does this UnorderedMultiDict NOT have the same keys, with values in the same order, as another?""" 262 return self.data != other.data
263
264 - def __repr__(self):
265 return "<UnorderedMultiDict %s>" % (self.data,)
266
267 - def __setitem__(self, key, value):
268 """Add a new key/value pair 269 270 If the key already exists, replaces the existing value 271 so that d[key] is the new value and not the old one. 272 273 To get all values for a given key, use d.getall(key). 274 """ 275 self.data.setdefault(key, []).append(value)
276
277 - def __delitem__(self, key):
278 """Remove all values for the given key""" 279 del self.data[key]
280
281 - def allkeys(self):
282 """iterate over all keys in arbitrary order""" 283 for k, v in self.data.iteritems(): 284 for x in v: 285 yield k
286
287 - def allvalues(self):
288 """iterate over all values in arbitrary order""" 289 for v in self.data.itervalues(): 290 for x in v: 291 yield x
292
293 - def allitems(self):
294 """iterate over all key/value pairs, in arbitrary order 295 296 Actually, the keys are iterated in arbitrary order but all 297 values for that key are iterated at sequence of addition 298 to the UnorderedMultiDict. 299 300 """ 301 for k, v in self.data.iteritems(): 302 for x in v: 303 yield (k, x)
304 305 __test__ = { 306 "test_ordered_multidict": """ 307 >>> od = OrderedMultiDict() 308 >>> od["Name"] = "Andrew" 309 >>> od["Color"] = "BLUE" 310 >>> od["Name"] = "Dalke" 311 >>> od["Color"] = "Green" 312 >>> od[3] = 9 313 >>> len(od) 314 3 315 >>> len(od.keys()) 316 3 317 >>> len(od.values()) 318 3 319 >>> len(od.items()) 320 3 321 >>> od.keys() 322 ['Color', 3, 'Name'] 323 >>> "Name" in od and "Name" in od.keys() and "Name" in od.allkeys() 324 1 325 >>> "Color" in od and "Color" in od.keys() and "Color" in od.allkeys() 326 1 327 >>> 3 in od and 3 in od.keys() and 3 in od.allkeys() 328 1 329 >>> od == od 330 1 331 >>> od != OrderedMultiDict() # line 25 332 1 333 >>> list(od.allkeys()) 334 ['Name', 'Color', 'Name', 'Color', 3] 335 >>> list(od.allvalues()) 336 ['Andrew', 'BLUE', 'Dalke', 'Green', 9] 337 >>> list(od.allitems()) 338 [('Name', 'Andrew'), ('Color', 'BLUE'), ('Name', 'Dalke'), ('Color', 'Green'), (3, 9)] 339 >>> len(list(od)) 340 3 341 >>> od["invalid"] 342 Traceback (most recent call last): 343 File "<stdin>", line 1, in ? 344 File "MultiDict.py", line 33, in __getitem__ 345 return self.data[key] 346 KeyError: invalid 347 >>> od["Color"] 348 'Green' 349 >>> od.getall("Color") 350 ['BLUE', 'Green'] 351 >>> od2 = OrderedMultiDict(od) 352 >>> list(od2.allitems()) 353 [('Name', 'Andrew'), ('Color', 'BLUE'), ('Name', 'Dalke'), ('Color', 'Green'), (3, 9)] 354 >>> od == od2 355 1 356 >>> od2 == od # line 53 357 1 358 >>> od2 != od 359 0 360 >>> del od["Color"] 361 >>> od["Color"] 362 Traceback (most recent call last): 363 File "<stdin>", line 1, in ? 364 File "MultiDict.py", line 33, in __getitem__ 365 return self.data[key] 366 KeyError: Color 367 >>> list(od.allitems()) 368 [('Name', 'Andrew'), ('Name', 'Dalke'), (3, 9)] 369 >>> list(od2.allkeys()) 370 ['Name', 'Color', 'Name', 'Color', 3] 371 >>> od2["Color"] 372 'Green' 373 >>> od == od2 374 0 375 >>> 376 >>> s = str(od2) 377 >>> s = repr(od2) 378 """, 379 "test_unordered_multidict": """ 380 >>> ud = UnorderedMultiDict() 381 >>> ud["Name"] = "Andrew" 382 >>> ud["Color"] = "BLUE" 383 >>> ud["Name"] = "Dalke" 384 >>> ud["Color"] = "GREEN" 385 >>> ud[3] = 9 386 >>> ud[3] 387 9 388 >>> ud["Name"] 389 'Dalke' 390 >>> ud["Color"] # line 11 391 'GREEN' 392 >>> ud[3] 393 9 394 >>> len(ud) 395 3 396 >>> len(list(ud)), len(ud.keys()), len(ud.values()), len(ud.items()) 397 (3, 3, 3, 3) 398 >>> ud["invalid"] 399 Traceback (most recent call last): 400 File "<stdin>", line 1, in ? 401 File "MultiDict.py", line 105, in __getitem__ 402 return self.data[key][-1] 403 KeyError: invalid 404 >>> ud.get("invalid") 405 >>> ud.get("invalid") is None 406 1 407 >>> ud.get("invalid", "red") 408 'red' 409 >>> "Color" in ud 410 1 411 >>> "Color" in ud.keys() # line 32 412 1 413 >>> "invalid" in ud 414 0 415 >>> "invalid" in ud.keys() 416 0 417 >>> ud.get("Color", "red") 418 'GREEN' 419 >>> "Andrew" in ud.values() 420 0 421 >>> "Dalke" in ud.values() 422 1 423 >>> ud.getall("Color") # line 44 424 ['BLUE', 'GREEN'] 425 >>> ud.getall("invalid") 426 Traceback (most recent call last): 427 File "<stdin>", line 1, in ? 428 File "MultiDict.py", line 126, in __getitem__ 429 return self.data[key] 430 KeyError: invalid 431 >>> len(list(ud.allkeys())), len(list(ud.allvalues())), len(list(ud.allitems())) 432 (5, 5, 5) 433 >>> ("Color", "BLUE") in ud.allitems() 434 1 435 >>> ("Color", "GREEN") in ud.allitems() 436 1 437 >>> ("Name", "Andrew") in ud.allitems() # line 58 438 1 439 >>> ("Name", "Dalke") in ud.allitems() 440 1 441 >>> (3, 9) in ud.allitems() 442 1 443 >>> x = list(ud.allkeys()) 444 >>> x.sort() 445 >>> x 446 [3, 'Color', 'Color', 'Name', 'Name'] 447 >>> x = list(ud.allvalues()) 448 >>> x.sort() 449 >>> x 450 [9, 'Andrew', 'BLUE', 'Dalke', 'GREEN'] 451 >>> x = list(ud) 452 >>> x.sort() 453 >>> x 454 [3, 'Color', 'Name'] 455 >>> ud2 = UnorderedMultiDict(ud) # line 76 456 >>> ud == ud2 457 1 458 >>> ud != ud 459 0 460 >>> del ud["Color"] 461 >>> ud == ud2 462 0 463 >>> ud != ud2 464 1 465 >>> len(ud) 466 2 467 >>> "Color" in ud 468 0 469 >>> "Color" in ud2 # line 90 470 1 471 >>> s = str(ud2) 472 >>> s = repr(ud2) 473 """, 474 "__doc__": __doc__, 475 } 476
477 -def _test():
478 import doctest, MultiDict 479 return doctest.testmod(MultiDict)
480 481 if __name__ == "__main__": 482 _test() 483