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