Package iceprod :: Package core :: Module odict
[hide private]
[frames] | no frames]

Source Code for Module iceprod.core.odict

   1  # odict.py 
   2  # An Ordered Dictionary object 
   3  # Copyright (C) 2005 Nicola Larosa, Michael Foord 
   4  # E-mail: nico-NoSp@m-tekNico.net, fuzzyman AT voidspace DOT org DOT uk 
   5   
   6  # This software is licensed under the terms of the BSD license. 
   7  # http://www.voidspace.org.uk/python/license.shtml 
   8  # Basically you're free to copy, modify, distribute and relicense it, 
   9  # So long as you keep a copy of the license with it. 
  10   
  11  # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml 
  12  # For information about bugfixes, updates and support, please join the 
  13  # Pythonutils mailing list: 
  14  # http://groups.google.com/group/pythonutils/ 
  15  # Comments, suggestions and bug reports welcome. 
  16  from __future__ import generators 
  17  from warnings import warn 
  18  from types import SliceType 
  19   
  20  import sys 
  21   
  22  """A dict that keeps keys in insertion order""" 
  23   
  24  __author__ = ('Nicola Larosa <nico-NoSp@m-tekNico.net>,' 
  25      'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>') 
  26   
  27  __docformat__ = "restructuredtext en" 
  28   
  29  __revision__ = '$Id: odict.py 129 2005-09-12 18:15:28Z teknico $' 
  30   
  31  __version__ = '0.2.1' 
  32   
  33  __all__ = ['OrderedDict', 'SequenceOrderedDict'] 
  34   
  35  INTP_VER = sys.version_info[:2] 
  36  if INTP_VER < (2, 2): 
  37      raise RuntimeError("Python v.2.2 or later needed") 
  38   
39 -class OrderedDict(dict):
40 """ 41 A class of dictionary that keeps the insertion order of keys. 42 43 All appropriate methods return keys, items, or values in an ordered way. 44 45 All normal dictionary methods are available. Update and comparison is 46 restricted to other OrderedDict objects. 47 48 Various sequence methods are available, including the ability to explicitly 49 mutate the key ordering. 50 51 __contains__ tests: 52 53 >>> d = OrderedDict(((1, 3),)) 54 >>> 1 in d 55 1 56 >>> 4 in d 57 0 58 59 __getitem__ tests: 60 61 >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2] 62 1 63 >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4] 64 Traceback (most recent call last): 65 KeyError: 4 66 67 __len__ tests: 68 69 >>> len(OrderedDict()) 70 0 71 >>> len(OrderedDict(((1, 3), (3, 2), (2, 1)))) 72 3 73 74 get tests: 75 76 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 77 >>> d.get(1) 78 3 79 >>> d.get(4) is None 80 1 81 >>> d.get(4, 5) 82 5 83 >>> d 84 OrderedDict([(1, 3), (3, 2), (2, 1)]) 85 86 has_key tests: 87 88 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 89 >>> d.has_key(1) 90 1 91 >>> d.has_key(4) 92 0 93 """ 94
95 - def __init__(self, init_val=(), strict=False):
96 """ 97 Create a new ordered dictionary. Cannot init from a normal dict, 98 nor from kwargs, since items order is undefined in those cases. 99 100 If the ``strict`` keyword argument is ``True`` (``False`` is the 101 default) then when doing slice assignment - the ``OrderedDict`` you are 102 assigning from *must not* contain any keys in the remaining dict. 103 104 >>> OrderedDict() 105 OrderedDict([]) 106 >>> OrderedDict({1: 1}) 107 Traceback (most recent call last): 108 TypeError: undefined order, cannot get items from dict 109 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 110 >>> d 111 OrderedDict([(1, 3), (3, 2), (2, 1)]) 112 >>> OrderedDict(d) 113 OrderedDict([(1, 3), (3, 2), (2, 1)]) 114 """ 115 self.strict = strict 116 dict.__init__(self) 117 if isinstance(init_val, OrderedDict): 118 self._sequence = init_val.keys() 119 dict.update(self, init_val) 120 elif isinstance(init_val, dict): 121 # we lose compatibility with other ordered dict types this way 122 raise TypeError('undefined order, cannot get items from dict') 123 else: 124 self._sequence = [] 125 # FIXME: efficiency ? 126 # NOTE: Can't just do ``dict.__update__`` since duplicate items 127 # would be chosen in an arbitrary order, we need to guarantee 128 # that the last one is used. 129 idx = 0 130 # sequence of 2-item sequences, or error 131 for item in init_val: 132 try: 133 key, val = item 134 except TypeError: 135 raise TypeError('cannot convert dictionary update' 136 ' sequence element #%d to a sequence' % idx) 137 self[key] = val 138 idx += 1
139 140 ### Special methods ### 141
142 - def __delitem__(self, key):
143 """ 144 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 145 >>> del d[3] 146 >>> d 147 OrderedDict([(1, 3), (2, 1)]) 148 >>> del d[3] 149 Traceback (most recent call last): 150 KeyError: 3 151 >>> d[3] = 2 152 >>> d 153 OrderedDict([(1, 3), (2, 1), (3, 2)]) 154 >>> del d[0:1] 155 >>> d 156 OrderedDict([(2, 1), (3, 2)]) 157 """ 158 if isinstance(key, SliceType): 159 # FIXME: efficiency ? 160 keys = self._sequence[key] 161 for entry in keys: 162 dict.__delitem__(self, entry) 163 del self._sequence[key] 164 else: 165 # do the dict.__delitem__ *first* as it raises 166 # the more appropriate error 167 dict.__delitem__(self, key) 168 self._sequence.remove(key)
169
170 - def __eq__(self, other):
171 """ 172 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 173 >>> d == OrderedDict(d) 174 1 175 >>> d == OrderedDict(((1, 3), (2, 1), (3, 2))) 176 0 177 >>> d == OrderedDict(((1, 0), (3, 2), (2, 1))) 178 0 179 >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) 180 0 181 >>> d == dict(d) 182 Traceback (most recent call last): 183 TypeError: Equality undefined for OrderedDicts and dictionaries 184 >>> d == False 185 0 186 """ 187 if isinstance(other, dict): 188 if not isinstance(other, OrderedDict): 189 raise TypeError('Equality undefined for OrderedDicts and ' 190 'dictionaries') 191 # FIXME: efficiency ? 192 # Generate both item lists for each compare 193 return (self.items() == other.items()) 194 else: 195 return False
196
197 - def __lt__(self, other):
198 """ 199 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 200 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) 201 >>> c < d 202 1 203 >>> d < c 204 0 205 >>> d < dict(c) 206 Traceback (most recent call last): 207 TypeError: Can only compare with other OrderedDicts 208 """ 209 if not isinstance(other, OrderedDict): 210 raise TypeError('Can only compare with other OrderedDicts') 211 # FIXME: efficiency ? 212 # Generate both item lists for each compare 213 return (self.items() < other.items())
214
215 - def __le__(self, other):
216 """ 217 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 218 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) 219 >>> e = OrderedDict(d) 220 >>> c <= d 221 1 222 >>> d <= c 223 0 224 >>> d <= dict(c) 225 Traceback (most recent call last): 226 TypeError: Can only compare with other OrderedDicts 227 >>> d <= e 228 1 229 """ 230 if not isinstance(other, OrderedDict): 231 raise TypeError('Can only compare with other OrderedDicts') 232 # FIXME: efficiency ? 233 # Generate both item lists for each compare 234 return (self.items() <= other.items())
235
236 - def __ne__(self, other):
237 """ 238 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 239 >>> d != OrderedDict(d) 240 0 241 >>> d != OrderedDict(((1, 3), (2, 1), (3, 2))) 242 1 243 >>> d != OrderedDict(((1, 0), (3, 2), (2, 1))) 244 1 245 >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) 246 0 247 >>> d != dict(d) 248 Traceback (most recent call last): 249 TypeError: Inequality undefined for OrderedDicts and dictionaries 250 >>> d != False 251 1 252 """ 253 if isinstance(other, dict): 254 if not isinstance(other, OrderedDict): 255 raise TypeError('Inequality undefined for OrderedDicts and ' 256 'dictionaries') 257 # FIXME: efficiency ? 258 # Generate both item lists for each compare 259 return not (self.items() == other.items()) 260 else: 261 return True
262
263 - def __gt__(self, other):
264 """ 265 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 266 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) 267 >>> d > c 268 1 269 >>> c > d 270 0 271 >>> d > dict(c) 272 Traceback (most recent call last): 273 TypeError: Can only compare with other OrderedDicts 274 """ 275 if not isinstance(other, OrderedDict): 276 raise TypeError('Can only compare with other OrderedDicts') 277 # FIXME: efficiency ? 278 # Generate both item lists for each compare 279 return (self.items() > other.items())
280
281 - def __ge__(self, other):
282 """ 283 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 284 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) 285 >>> e = OrderedDict(d) 286 >>> c >= d 287 0 288 >>> d >= c 289 1 290 >>> d >= dict(c) 291 Traceback (most recent call last): 292 TypeError: Can only compare with other OrderedDicts 293 >>> e >= d 294 1 295 """ 296 if not isinstance(other, OrderedDict): 297 raise TypeError('Can only compare with other OrderedDicts') 298 # FIXME: efficiency ? 299 # Generate both item lists for each compare 300 return (self.items() >= other.items())
301
302 - def __repr__(self):
303 """ 304 Used for __repr__ and __str__ 305 306 >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) 307 >>> r1 308 "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])" 309 >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) 310 >>> r2 311 "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])" 312 >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) 313 1 314 >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) 315 1 316 """ 317 return '%s([%s])' % (self.__class__.__name__, ', '.join( 318 ['(%r, %r)' % (key, self[key]) for key in self._sequence]))
319
320 - def __setitem__(self, key, val):
321 """ 322 Allows slice assignment, so long as the slice is an OrderedDict 323 >>> d = OrderedDict() 324 >>> d['a'] = 'b' 325 >>> d['b'] = 'a' 326 >>> d[3] = 12 327 >>> d 328 OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)]) 329 >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4))) 330 >>> d 331 OrderedDict([(1, 2), (2, 3), (3, 4)]) 332 >>> d[::2] = OrderedDict(((7, 8), (9, 10))) 333 >>> d 334 OrderedDict([(7, 8), (2, 3), (9, 10)]) 335 >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4))) 336 >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) 337 >>> d 338 OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) 339 >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True) 340 >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) 341 >>> d 342 OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) 343 344 >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True) 345 >>> a[3] = 4 346 >>> a 347 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 348 >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 349 >>> a 350 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 351 >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]) 352 Traceback (most recent call last): 353 ValueError: slice assignment must be from unique keys 354 >>> a = OrderedDict(((0, 1), (1, 2), (2, 3))) 355 >>> a[3] = 4 356 >>> a 357 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 358 >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 359 >>> a 360 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 361 >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 362 >>> a 363 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 364 >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 365 >>> a 366 OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)]) 367 368 >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 369 >>> d[:1] = 3 370 Traceback (most recent call last): 371 TypeError: slice assignment requires an OrderedDict 372 373 >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) 374 >>> d[:1] = OrderedDict([(9, 8)]) 375 >>> d 376 OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)]) 377 """ 378 if isinstance(key, SliceType): 379 if not isinstance(val, OrderedDict): 380 # FIXME: allow a list of tuples ? 381 raise TypeError('slice assignment requires an OrderedDict') 382 keys = self._sequence[key] 383 indexes = range(len(self._sequence))[key] 384 if key.step is None: 385 # NOTE: new slice may not be the same size as the one being 386 # overwritten ! 387 # NOTE: What is the algorithm for an impossible slice ? 388 # e.g. d[5:3] 389 pos = key.start or 0 390 del self[key] 391 newkeys = val.keys() 392 for k in newkeys: 393 if k in self: 394 if self.strict: 395 raise ValueError('slice assignment must be from ' 396 'unique keys') 397 else: 398 # NOTE: This removes duplicate keys *first* 399 # so start position might have changed ? 400 del self[k] 401 self._sequence = (self._sequence[:pos] + newkeys + 402 self._sequence[pos:]) 403 dict.update(self, val) 404 else: 405 # extended slice 406 # length of new slice must be the same as the one being 407 # replaced 408 if len(keys) != len(val): 409 raise ValueError('attempt to assign sequence of size %s ' 410 'to extended slice of size %s' % (len(val), len(keys))) 411 # FIXME: efficiency ? 412 del self[key] 413 item_list = zip(indexes, val.items()) 414 # smallest indexes first - higher indexes not guaranteed to 415 # exist 416 item_list.sort() 417 for pos, (newkey, newval) in item_list: 418 if self.strict and newkey in self: 419 raise ValueError('slice assignment must be from unique' 420 ' keys') 421 self.insert(pos, newkey, newval) 422 else: 423 if not self.has_key(key): 424 self._sequence.append(key) 425 dict.__setitem__(self, key, val)
426
427 - def __getitem__(self, key):
428 """ 429 Allows slicing. Returns an OrderedDict if you slice. 430 >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)]) 431 >>> b[::-1] 432 OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)]) 433 >>> b[2:5] 434 OrderedDict([(5, 2), (4, 3), (3, 4)]) 435 >>> type(b[2:4]) 436 <class '__main__.OrderedDict'> 437 """ 438 if isinstance(key, SliceType): 439 # FIXME: does this raise the error we want ? 440 keys = self._sequence[key] 441 # FIXME: efficiency ? 442 return OrderedDict([(entry, self[entry]) for entry in keys]) 443 else: 444 return dict.__getitem__(self, key)
445 446 __str__ = __repr__ 447
448 - def __setattr__(self, name, value):
449 """ 450 Implemented so that accesses to ``sequence`` raise a warning and are 451 diverted to the new ``setkeys`` method. 452 """ 453 if name == 'sequence': 454 warn('use of sequence attribute is deprecated. Use keys method ' 455 'instead.', DeprecationWarning) 456 # NOTE: doesn't return anything 457 self.setkeys(value) 458 else: 459 # FIXME: do we want to allow arbitrary setting of attributes ? 460 # or do we want to manage it ? 461 object.__setattr__(self, name, value)
462
463 - def __getattr__(self, name):
464 """ 465 Implemented so that access to ``sequence`` raises a warning. 466 467 >>> d = OrderedDict() 468 >>> d.sequence 469 [] 470 """ 471 if name == 'sequence': 472 warn('use of sequence attribute is deprecated. Use keys method ' 473 'instead.', DeprecationWarning) 474 # NOTE: Still (currently) returns a direct reference. Need to 475 # because code that uses sequence will expect to be able to 476 # mutate it in place. 477 return self._sequence 478 else: 479 # raise the appropriate error 480 raise AttributeError("OrderedDict has no attribute '%s'" % name)
481
482 - def __deepcopy__(self, memo):
483 """ 484 To allow deepcopy to work with OrderedDict. 485 486 >>> from copy import deepcopy 487 >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)]) 488 >>> a['test'] = {} 489 >>> b = deepcopy(a) 490 >>> b == a 491 1 492 >>> b is a 493 0 494 >>> a['test'] is b['test'] 495 0 496 """ 497 from copy import deepcopy 498 return self.__class__(deepcopy(self.items(), memo), self.strict)
499 500 501 ### Read-only methods ### 502
503 - def copy(self):
504 """ 505 >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy() 506 OrderedDict([(1, 3), (3, 2), (2, 1)]) 507 """ 508 return OrderedDict(self)
509
510 - def items(self):
511 """ 512 ``items`` returns a list of tuples representing all the 513 ``(key, value)`` pairs in the dictionary. 514 515 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 516 >>> d.items() 517 [(1, 3), (3, 2), (2, 1)] 518 >>> d.clear() 519 >>> d.items() 520 [] 521 """ 522 return zip(self._sequence, self.values())
523
524 - def keys(self):
525 """ 526 Return a list of keys in the ``OrderedDict``. 527 528 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 529 >>> d.keys() 530 [1, 3, 2] 531 """ 532 return self._sequence[:]
533
534 - def values(self, values=None):
535 """ 536 Return a list of all the values in the OrderedDict. 537 538 Optionally you can pass in a list of values, which will replace the 539 current list. The value list must be the same len as the OrderedDict. 540 541 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 542 >>> d.values() 543 [3, 2, 1] 544 """ 545 return [self[key] for key in self._sequence]
546
547 - def iteritems(self):
548 """ 549 >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems() 550 >>> ii.next() 551 (1, 3) 552 >>> ii.next() 553 (3, 2) 554 >>> ii.next() 555 (2, 1) 556 >>> ii.next() 557 Traceback (most recent call last): 558 StopIteration 559 """ 560 def make_iter(self=self): 561 keys = self.iterkeys() 562 while True: 563 key = keys.next() 564 yield (key, self[key])
565 return make_iter()
566
567 - def iterkeys(self):
568 """ 569 >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys() 570 >>> ii.next() 571 1 572 >>> ii.next() 573 3 574 >>> ii.next() 575 2 576 >>> ii.next() 577 Traceback (most recent call last): 578 StopIteration 579 """ 580 return iter(self._sequence)
581 582 __iter__ = iterkeys 583
584 - def itervalues(self):
585 """ 586 >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues() 587 >>> iv.next() 588 3 589 >>> iv.next() 590 2 591 >>> iv.next() 592 1 593 >>> iv.next() 594 Traceback (most recent call last): 595 StopIteration 596 """ 597 def make_iter(self=self): 598 keys = self.iterkeys() 599 while True: 600 yield self[keys.next()]
601 return make_iter() 602 603 ### Read-write methods ### 604
605 - def clear(self):
606 """ 607 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 608 >>> d.clear() 609 >>> d 610 OrderedDict([]) 611 """ 612 dict.clear(self) 613 self._sequence = []
614
615 - def pop(self, key, *args):
616 """ 617 No dict.pop in Python 2.2, gotta reimplement it 618 619 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 620 >>> d.pop(3) 621 2 622 >>> d 623 OrderedDict([(1, 3), (2, 1)]) 624 >>> d.pop(4) 625 Traceback (most recent call last): 626 KeyError: 4 627 >>> d.pop(4, 0) 628 0 629 >>> d.pop(4, 0, 1) 630 Traceback (most recent call last): 631 TypeError: pop expected at most 2 arguments, got 3 632 """ 633 if len(args) > 1: 634 raise TypeError, ('pop expected at most 2 arguments, got %s' % 635 (len(args) + 1)) 636 if key in self: 637 val = self[key] 638 del self[key] 639 else: 640 try: 641 val = args[0] 642 except IndexError: 643 raise KeyError(key) 644 return val
645
646 - def popitem(self, i=-1):
647 """ 648 Delete and return an item specified by index, not a random one as in 649 dict. The index is -1 by default (the last item). 650 651 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 652 >>> d.popitem() 653 (2, 1) 654 >>> d 655 OrderedDict([(1, 3), (3, 2)]) 656 >>> d.popitem(0) 657 (1, 3) 658 >>> OrderedDict().popitem() 659 Traceback (most recent call last): 660 KeyError: 'popitem(): dictionary is empty' 661 >>> d.popitem(2) 662 Traceback (most recent call last): 663 IndexError: popitem(): index 2 not valid 664 """ 665 if not self._sequence: 666 raise KeyError('popitem(): dictionary is empty') 667 try: 668 key = self._sequence[i] 669 except IndexError: 670 raise IndexError('popitem(): index %s not valid' % i) 671 return (key, self.pop(key))
672
673 - def setdefault(self, key, defval = None):
674 """ 675 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 676 >>> d.setdefault(1) 677 3 678 >>> d.setdefault(4) is None 679 1 680 >>> d 681 OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)]) 682 >>> d.setdefault(5, 0) 683 0 684 >>> d 685 OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)]) 686 """ 687 if key in self: 688 return self[key] 689 else: 690 self[key] = defval 691 return defval
692
693 - def update(self, from_od):
694 """ 695 Update from another OrderedDict or sequence of (key, value) pairs 696 697 >>> d = OrderedDict() 698 >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1)))) 699 >>> d 700 OrderedDict([(1, 3), (3, 2), (2, 1)]) 701 >>> d.update({4: 4}) 702 Traceback (most recent call last): 703 TypeError: undefined order, cannot get items from dict 704 >>> d.update((4, 4)) 705 Traceback (most recent call last): 706 TypeError: cannot convert dictionary update sequence element #0 to a sequence 707 """ 708 if isinstance(from_od, OrderedDict): 709 for key, val in from_od.items(): 710 self[key] = val 711 elif isinstance(from_od, dict): 712 # we lose compatibility with other ordered dict types this way 713 raise TypeError('undefined order, cannot get items from dict') 714 else: 715 idx = 0 716 # sequence of 2-item sequences, or error 717 for item in from_od: 718 try: 719 key, val = item 720 except TypeError: 721 raise TypeError('cannot convert dictionary update' 722 ' sequence element #%d to a sequence' % idx) 723 self[key] = val 724 idx += 1
725
726 - def setitems(self, items):
727 """ 728 This method allows you to set the items in the dict. 729 730 It takes a list of tuples - of the same sort returned by the ``items`` 731 method. 732 733 >>> d = OrderedDict() 734 >>> d.setitems(((3, 1), (2, 3), (1, 2))) 735 >>> d 736 OrderedDict([(3, 1), (2, 3), (1, 2)]) 737 """ 738 self.clear() 739 # FIXME: this allows you to pass in an OrderedDict as well :-) 740 self.update(items)
741
742 - def setkeys(self, keys):
743 """ 744 ``setkeys`` all ows you to pass in a new list of keys which will 745 replace the current set. This must contain the same set of keys, but 746 need not be in the same order. 747 748 If you pass in new keys that don't match, a ``KeyError`` will be 749 raised. 750 751 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 752 >>> d.keys() 753 [1, 3, 2] 754 >>> d.setkeys((1, 2, 3)) 755 >>> d 756 OrderedDict([(1, 3), (2, 1), (3, 2)]) 757 >>> d.setkeys(['a', 'b', 'c']) 758 Traceback (most recent call last): 759 KeyError: 'Keylist is not the same as current keylist.' 760 """ 761 # FIXME: Efficiency ? (use set for Python 2.4 :-) 762 # NOTE: list(keys) rather than keys[:] because keys[:] returns 763 # a tuple, if keys is a tuple. 764 kcopy = list(keys) 765 kcopy.sort() 766 self._sequence.sort() 767 if kcopy != self._sequence: 768 raise KeyError('Keylist is not the same as current keylist.') 769 # NOTE: This makes the _sequence attribute a new object, instead 770 # of changing it in place. 771 # FIXME: efficiency ? 772 self._sequence = list(keys)
773
774 - def setvalues(self, values):
775 """ 776 You can pass in a list of values, which will replace the 777 current list. The value list must be the same len as the OrderedDict. 778 779 (Or a ``ValueError`` is raised.) 780 781 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 782 >>> d.setvalues((1, 2, 3)) 783 >>> d 784 OrderedDict([(1, 1), (3, 2), (2, 3)]) 785 >>> d.setvalues([6]) 786 Traceback (most recent call last): 787 ValueError: Value list is not the same length as the OrderedDict. 788 """ 789 if len(values) != len(self): 790 # FIXME: correct error to raise ? 791 raise ValueError('Value list is not the same length as the ' 792 'OrderedDict.') 793 self.update(zip(self, values))
794 795 ### Sequence Methods ### 796
797 - def index(self, key):
798 """ 799 Return the position of the specified key in the OrderedDict. 800 801 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 802 >>> d.index(3) 803 1 804 >>> d.index(4) 805 Traceback (most recent call last): 806 ValueError: list.index(x): x not in list 807 """ 808 return self._sequence.index(key)
809
810 - def insert(self, index, key, value):
811 """ 812 Takes ``index``, ``key``, and ``value`` as arguments. 813 814 Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in 815 the OrderedDict. 816 817 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 818 >>> d.insert(0, 4, 0) 819 >>> d 820 OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)]) 821 >>> d.insert(0, 2, 1) 822 >>> d 823 OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)]) 824 >>> d.insert(8, 8, 1) 825 >>> d 826 OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)]) 827 """ 828 if key in self: 829 # FIXME: efficiency ? 830 del self[key] 831 self._sequence.insert(index, key) 832 dict.__setitem__(self, key, value)
833
834 - def reverse(self):
835 """ 836 Reverse the order of the OrderedDict. 837 838 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) 839 >>> d.reverse() 840 >>> d 841 OrderedDict([(2, 1), (3, 2), (1, 3)]) 842 """ 843 self._sequence.reverse()
844
845 - def sort(self, *args, **kwargs):
846 """ 847 Sort the key order in the OrderedDict. 848 849 This method takes the same arguments as the ``list.sort`` method on 850 your version of Python. 851 852 >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4))) 853 >>> d.sort() 854 >>> d 855 OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)]) 856 """ 857 self._sequence.sort(*args, **kwargs)
858
859 -class _keys(object):
860 # FIXME: should this object be a subclass of list ? 861 """ 862 Custom object for accessing the keys of an OrderedDict. 863 864 Can be called like the normal ``OrderedDict.keys`` method, but also 865 supports indexing and sequence methods. 866 """ 867
868 - def __init__(self, main):
869 self._main = main
870
871 - def __call__(self):
872 """Pretend to be the keys method.""" 873 return self._main._keys()
874
875 - def __getitem__(self, index):
876 """Fetch the key at position i.""" 877 # NOTE: this automatically supports slicing :-) 878 return self._main._sequence[index]
879
880 - def __setitem__(self, index, name):
881 """ 882 You cannot assign to keys, but you can do slice assignment to re-order 883 them. 884 885 You can only do slice assignment if the new set of keys is a reordering 886 of the original set. 887 """ 888 if isinstance(index, SliceType): 889 # FIXME: efficiency ? 890 # check length is the same 891 indexes = range(len(self._main._sequence))[index] 892 if len(indexes) != len(name): 893 raise ValueError('attempt to assign sequence of size %s ' 894 'to slice of size %s' % (len(name), len(indexes))) 895 # check they are the same keys 896 # FIXME: Use set 897 old_keys = self._main._sequence[index] 898 new_keys = list(name) 899 old_keys.sort() 900 new_keys.sort() 901 if old_keys != new_keys: 902 raise KeyError('Keylist is not the same as current keylist.') 903 orig_vals = [self._main[k] for k in name] 904 del self._main[index] 905 vals = zip(indexes, name, orig_vals) 906 vals.sort() 907 for i, k, v in vals: 908 if self._main.strict and k in self._main: 909 raise ValueError('slice assignment must be from ' 910 'unique keys') 911 self._main.insert(i, k, v) 912 else: 913 raise ValueError('Cannot assign to keys')
914 915 ### following methods pinched from UserList and adapted ###
916 - def __repr__(self): return repr(self._main._sequence)
917 918 # FIXME: do we need to check if we are comparing with another ``_keys`` 919 # object ? (like the __cast method of UserList)
920 - def __lt__(self, other): return self._main._sequence < other
921 - def __le__(self, other): return self._main._sequence <= other
922 - def __eq__(self, other): return self._main._sequence == other
923 - def __ne__(self, other): return self._main._sequence != other
924 - def __gt__(self, other): return self._main._sequence > other
925 - def __ge__(self, other): return self._main._sequence >= other
926 # FIXME: do we need __cmp__ as well as rich comparisons ?
927 - def __cmp__(self, other): return cmp(self._main._sequence, other)
928
929 - def __contains__(self, item): return item in self._main._sequence
930 - def __len__(self): return len(self._main._sequence)
931 - def __iter__(self): return self._main.iterkeys()
932 - def count(self, item): return self._main._sequence.count(item)
933 - def index(self, item, *args): return self._main._sequence.index(item, *args)
934 - def reverse(self): self._main._sequence.reverse()
935 - def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds)
936 - def __mul__(self, n): return self._main._sequence*n
937 __rmul__ = __mul__
938 - def __add__(self, other): return self._main._sequence + other
939 - def __radd__(self, other): return other + self._main._sequence
940 941 ## following methods not implemented for keys ##
942 - def __delitem__(self, i): raise TypeError('Can\'t delete items from keys')
943 - def __iadd__(self, other): raise TypeError('Can\'t add in place to keys')
944 - def __imul__(self, n): raise TypeError('Can\'t multiply keys in place')
945 - def append(self, item): raise TypeError('Can\'t append items to keys')
946 - def insert(self, i, item): raise TypeError('Can\'t insert items into keys')
947 - def pop(self, i=-1): raise TypeError('Can\'t pop items from keys')
948 - def remove(self, item): raise TypeError('Can\'t remove items from keys')
949 - def extend(self, other): raise TypeError('Can\'t extend keys')
950
951 -class _items(object):
952 """ 953 Custom object for accessing the items of an OrderedDict. 954 955 Can be called like the normal ``OrderedDict.items`` method, but also 956 supports indexing and sequence methods. 957 """ 958
959 - def __init__(self, main):
960 self._main = main
961
962 - def __call__(self):
963 """Pretend to be the items method.""" 964 return self._main._items()
965
966 - def __getitem__(self, index):
967 """Fetch the item at position i.""" 968 if isinstance(index, SliceType): 969 # fetching a slice returns an OrderedDict 970 return self._main[index].items() 971 key = self._main._sequence[index] 972 return (key, self._main[key])
973
974 - def __setitem__(self, index, item):
975 """Set item at position i to item.""" 976 if isinstance(index, SliceType): 977 # NOTE: item must be an iterable (list of tuples) 978 self._main[index] = OrderedDict(item) 979 else: 980 # FIXME: Does this raise a sensible error ? 981 orig = self._main.keys[index] 982 key, value = item 983 if self._main.strict and key in self and (key != orig): 984 raise ValueError('slice assignment must be from ' 985 'unique keys') 986 # delete the current one 987 del self._main[self._main._sequence[index]] 988 self._main.insert(index, key, value)
989
990 - def __delitem__(self, i):
991 """Delete the item at position i.""" 992 key = self._main._sequence[i] 993 if isinstance(i, SliceType): 994 for k in key: 995 # FIXME: efficiency ? 996 del self._main[k] 997 else: 998 del self._main[key]
999 1000 ### following methods pinched from UserList and adapted ###
1001 - def __repr__(self): return repr(self._main.items())
1002 1003 # FIXME: do we need to check if we are comparing with another ``_items`` 1004 # object ? (like the __cast method of UserList)
1005 - def __lt__(self, other): return self._main.items() < other
1006 - def __le__(self, other): return self._main.items() <= other
1007 - def __eq__(self, other): return self._main.items() == other
1008 - def __ne__(self, other): return self._main.items() != other
1009 - def __gt__(self, other): return self._main.items() > other
1010 - def __ge__(self, other): return self._main.items() >= other
1011 - def __cmp__(self, other): return cmp(self._main.items(), other)
1012
1013 - def __contains__(self, item): return item in self._main.items()
1014 - def __len__(self): return len(self._main._sequence) # easier :-)
1015 - def __iter__(self): return self._main.iteritems()
1016 - def count(self, item): return self._main.items().count(item)
1017 - def index(self, item, *args): return self._main.items().index(item, *args)
1018 - def reverse(self): self._main.reverse()
1019 - def sort(self, *args, **kwds): self._main.sort(*args, **kwds)
1020 - def __mul__(self, n): return self._main.items()*n
1021 __rmul__ = __mul__
1022 - def __add__(self, other): return self._main.items() + other
1023 - def __radd__(self, other): return other + self._main.items()
1024
1025 - def append(self, item):
1026 """Add an item to the end.""" 1027 # FIXME: this is only append if the key isn't already present 1028 key, value = item 1029 self._main[key] = value
1030
1031 - def insert(self, i, item):
1032 key, value = item 1033 self._main.insert(i, key, value)
1034
1035 - def pop(self, i=-1):
1036 key = self._main._sequence[i] 1037 return (key, self._main.pop(key))
1038
1039 - def remove(self, item):
1040 key, value = item 1041 try: 1042 assert value == self._main[key] 1043 except (KeyError, AssertionError): 1044 raise ValueError('ValueError: list.remove(x): x not in list') 1045 else: 1046 del self._main[key]
1047
1048 - def extend(self, other):
1049 # FIXME: is only a true extend if none of the keys already present 1050 for item in other: 1051 key, value = item 1052 self._main[key] = value
1053
1054 - def __iadd__(self, other):
1055 self.extend(other)
1056 1057 ## following methods not implemented for items ## 1058
1059 - def __imul__(self, n): raise TypeError('Can\'t multiply items in place')
1060 1061
1062 -class _values(object):
1063 """ 1064 Custom object for accessing the values of an OrderedDict. 1065 1066 Can be called like the normal ``OrderedDict.values`` method, but also 1067 supports indexing and sequence methods. 1068 """ 1069
1070 - def __init__(self, main):
1071 self._main = main
1072
1073 - def __call__(self):
1074 """Pretend to be the values method.""" 1075 return self._main._values()
1076
1077 - def __getitem__(self, index):
1078 """Fetch the value at position i.""" 1079 if isinstance(index, SliceType): 1080 return [self._main[key] for key in self._main._sequence[index]] 1081 else: 1082 return self._main[self._main._sequence[index]]
1083
1084 - def __setitem__(self, index, value):
1085 """ 1086 Set the value at position i to value. 1087 1088 You can only do slice assignment to values if you supply a sequence of 1089 equal length to the slice you are replacing. 1090 """ 1091 if isinstance(index, SliceType): 1092 keys = self._main._sequence[index] 1093 if len(keys) != len(value): 1094 raise ValueError('attempt to assign sequence of size %s ' 1095 'to slice of size %s' % (len(name), len(keys))) 1096 # FIXME: efficiency ? Would be better to calculate the indexes 1097 # directly from the slice object 1098 # NOTE: the new keys can collide with existing keys (or even 1099 # contain duplicates) - these will overwrite 1100 for key, val in zip(keys, value): 1101 self._main[key] = val 1102 else: 1103 self._main[self._main._sequence[index]] = value
1104 1105 ### following methods pinched from UserList and adapted ###
1106 - def __repr__(self): return repr(self._main.values())
1107 1108 # FIXME: do we need to check if we are comparing with another ``_values`` 1109 # object ? (like the __cast method of UserList)
1110 - def __lt__(self, other): return self._main.values() < other
1111 - def __le__(self, other): return self._main.values() <= other
1112 - def __eq__(self, other): return self._main.values() == other
1113 - def __ne__(self, other): return self._main.values() != other
1114 - def __gt__(self, other): return self._main.values() > other
1115 - def __ge__(self, other): return self._main.values() >= other
1116 - def __cmp__(self, other): return cmp(self._main.values(), other)
1117
1118 - def __contains__(self, item): return item in self._main.values()
1119 - def __len__(self): return len(self._main._sequence) # easier :-)
1120 - def __iter__(self): return self._main.itervalues()
1121 - def count(self, item): return self._main.values().count(item)
1122 - def index(self, item, *args): return self._main.values().index(item, *args)
1123
1124 - def reverse(self):
1125 """Reverse the values""" 1126 vals = self._main.values() 1127 vals.reverse() 1128 # FIXME: efficiency 1129 self[:] = vals
1130
1131 - def sort(self, *args, **kwds):
1132 """Sort the values.""" 1133 vals = self._main.values() 1134 vals.sort(*args, **kwds) 1135 self[:] = vals
1136
1137 - def __mul__(self, n): return self._main.values()*n
1138 __rmul__ = __mul__
1139 - def __add__(self, other): return self._main.values() + other
1140 - def __radd__(self, other): return other + self._main.values()
1141 1142 ## following methods not implemented for values ##
1143 - def __delitem__(self, i): raise TypeError('Can\'t delete items from values')
1144 - def __iadd__(self, other): raise TypeError('Can\'t add in place to values')
1145 - def __imul__(self, n): raise TypeError('Can\'t multiply values in place')
1146 - def append(self, item): raise TypeError('Can\'t append items to values')
1147 - def insert(self, i, item): raise TypeError('Can\'t insert items into values')
1148 - def pop(self, i=-1): raise TypeError('Can\'t pop items from values')
1149 - def remove(self, item): raise TypeError('Can\'t remove items from values')
1150 - def extend(self, other): raise TypeError('Can\'t extend values')
1151 1152
1153 -class SequenceOrderedDict(OrderedDict):
1154 """ 1155 Experimental version of OrderedDict that has a custom object for ``keys``, 1156 ``values``, and ``items``. 1157 1158 These are callable sequence objects that work as methods, or can be 1159 manipulated directly as sequences. 1160 1161 Test for ``keys``, ``items`` and ``values``. 1162 1163 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) 1164 >>> d 1165 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) 1166 >>> d.keys 1167 [1, 2, 3] 1168 >>> d.keys() 1169 [1, 2, 3] 1170 >>> d.setkeys((3, 2, 1)) 1171 >>> d 1172 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) 1173 >>> d.setkeys((1, 2, 3)) 1174 >>> d.keys[0] 1175 1 1176 >>> d.keys[:] 1177 [1, 2, 3] 1178 >>> d.keys[-1] 1179 3 1180 >>> d.keys[-2] 1181 2 1182 >>> d.keys[0:2] = [2, 1] 1183 >>> d 1184 SequenceOrderedDict([(2, 3), (1, 2), (3, 4)]) 1185 >>> d.keys.reverse() 1186 >>> d.keys 1187 [3, 1, 2] 1188 >>> d.keys = [1, 2, 3] 1189 >>> d 1190 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) 1191 >>> d.keys = [3, 1, 2] 1192 >>> d 1193 SequenceOrderedDict([(3, 4), (1, 2), (2, 3)]) 1194 >>> a = SequenceOrderedDict() 1195 >>> b = SequenceOrderedDict() 1196 >>> a.keys == b.keys 1197 1 1198 >>> a['a'] = 3 1199 >>> a.keys == b.keys 1200 0 1201 >>> b['a'] = 3 1202 >>> a.keys == b.keys 1203 1 1204 >>> b['b'] = 3 1205 >>> a.keys == b.keys 1206 0 1207 >>> a.keys > b.keys 1208 0 1209 >>> a.keys < b.keys 1210 1 1211 >>> 'a' in a.keys 1212 1 1213 >>> len(b.keys) 1214 2 1215 >>> 'c' in d.keys 1216 0 1217 >>> 1 in d.keys 1218 1 1219 >>> [v for v in d.keys] 1220 [3, 1, 2] 1221 >>> d.keys.sort() 1222 >>> d.keys 1223 [1, 2, 3] 1224 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True) 1225 >>> d.keys[::-1] = [1, 2, 3] 1226 >>> d 1227 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) 1228 >>> d.keys[:2] 1229 [3, 2] 1230 >>> d.keys[:2] = [1, 3] 1231 Traceback (most recent call last): 1232 KeyError: 'Keylist is not the same as current keylist.' 1233 1234 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) 1235 >>> d 1236 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) 1237 >>> d.values 1238 [2, 3, 4] 1239 >>> d.values() 1240 [2, 3, 4] 1241 >>> d.setvalues((4, 3, 2)) 1242 >>> d 1243 SequenceOrderedDict([(1, 4), (2, 3), (3, 2)]) 1244 >>> d.values[::-1] 1245 [2, 3, 4] 1246 >>> d.values[0] 1247 4 1248 >>> d.values[-2] 1249 3 1250 >>> del d.values[0] 1251 Traceback (most recent call last): 1252 TypeError: Can't delete items from values 1253 >>> d.values[::2] = [2, 4] 1254 >>> d 1255 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) 1256 >>> 7 in d.values 1257 0 1258 >>> len(d.values) 1259 3 1260 >>> [val for val in d.values] 1261 [2, 3, 4] 1262 >>> d.values[-1] = 2 1263 >>> d.values.count(2) 1264 2 1265 >>> d.values.index(2) 1266 0 1267 >>> d.values[-1] = 7 1268 >>> d.values 1269 [2, 3, 7] 1270 >>> d.values.reverse() 1271 >>> d.values 1272 [7, 3, 2] 1273 >>> d.values.sort() 1274 >>> d.values 1275 [2, 3, 7] 1276 >>> d.values.append('anything') 1277 Traceback (most recent call last): 1278 TypeError: Can't append items to values 1279 >>> d.values = (1, 2, 3) 1280 >>> d 1281 SequenceOrderedDict([(1, 1), (2, 2), (3, 3)]) 1282 1283 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) 1284 >>> d 1285 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) 1286 >>> d.items() 1287 [(1, 2), (2, 3), (3, 4)] 1288 >>> d.setitems([(3, 4), (2 ,3), (1, 2)]) 1289 >>> d 1290 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) 1291 >>> d.items[0] 1292 (3, 4) 1293 >>> d.items[:-1] 1294 [(3, 4), (2, 3)] 1295 >>> d.items[1] = (6, 3) 1296 >>> d.items 1297 [(3, 4), (6, 3), (1, 2)] 1298 >>> d.items[1:2] = [(9, 9)] 1299 >>> d 1300 SequenceOrderedDict([(3, 4), (9, 9), (1, 2)]) 1301 >>> del d.items[1:2] 1302 >>> d 1303 SequenceOrderedDict([(3, 4), (1, 2)]) 1304 >>> (3, 4) in d.items 1305 1 1306 >>> (4, 3) in d.items 1307 0 1308 >>> len(d.items) 1309 2 1310 >>> [v for v in d.items] 1311 [(3, 4), (1, 2)] 1312 >>> d.items.count((3, 4)) 1313 1 1314 >>> d.items.index((1, 2)) 1315 1 1316 >>> d.items.index((2, 1)) 1317 Traceback (most recent call last): 1318 ValueError: list.index(x): x not in list 1319 >>> d.items.reverse() 1320 >>> d.items 1321 [(1, 2), (3, 4)] 1322 >>> d.items.reverse() 1323 >>> d.items.sort() 1324 >>> d.items 1325 [(1, 2), (3, 4)] 1326 >>> d.items.append((5, 6)) 1327 >>> d.items 1328 [(1, 2), (3, 4), (5, 6)] 1329 >>> d.items.insert(0, (0, 0)) 1330 >>> d.items 1331 [(0, 0), (1, 2), (3, 4), (5, 6)] 1332 >>> d.items.insert(-1, (7, 8)) 1333 >>> d.items 1334 [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)] 1335 >>> d.items.pop() 1336 (5, 6) 1337 >>> d.items 1338 [(0, 0), (1, 2), (3, 4), (7, 8)] 1339 >>> d.items.remove((1, 2)) 1340 >>> d.items 1341 [(0, 0), (3, 4), (7, 8)] 1342 >>> d.items.extend([(1, 2), (5, 6)]) 1343 >>> d.items 1344 [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)] 1345 """ 1346
1347 - def __init__(self, init_val=(), strict=True):
1348 OrderedDict.__init__(self, init_val, strict=strict) 1349 self._keys = self.keys 1350 self._values = self.values 1351 self._items = self.items 1352 self.keys = _keys(self) 1353 self.values = _values(self) 1354 self.items = _items(self) 1355 self._att_dict = {'keys': self.setkeys, 1356 'items': self.setitems, 1357 'values': self.setvalues 1358 }
1359
1360 - def __setattr__(self, name, value):
1361 """Protect keys, items, and values.""" 1362 if not '_att_dict' in self.__dict__: 1363 object.__setattr__(self, name, value) 1364 else: 1365 try: 1366 fun = self._att_dict[name] 1367 except KeyError: 1368 OrderedDict.__setattr__(self, name, value) 1369 else: 1370 fun(value)
1371 1372 if __name__ == '__main__': 1373 # run the code tests in doctest format 1374 import doctest 1375 m = sys.modules.get('__main__') 1376 globs = m.__dict__.copy() 1377 globs.update({ 1378 'INTP_VER': INTP_VER, 1379 }) 1380 doctest.testmod(m, globs=globs) 1381 1382 """ 1383 ISSUES 1384 ====== 1385 1386 Slicing doesn't work in Python 2.2. This is because in 2.2, you can't index 1387 a sequence with a slice object. Could be implemented with ``operator`` 1388 slicing functions (which don't support extended slices). 1389 1390 TODO 1391 ==== 1392 1393 Addition (``__add__``) ? (This would just be syntactic sugar for 1394 ``update``) 1395 1396 Implement the Python 2.4 arguments (``key`` and ``reverse``) for ``sort`` 1397 for Python 2.2 and 2.3 ? (So the interface is stable) 1398 1399 Add sequence methods ``move`` and ``rename`` ? (To change the name of a key 1400 at a specific index, and change the index of a key from one position to 1401 another) 1402 1403 Allow assignment to keys (in ``SequenceOrderedDict``) to rename a key ? 1404 1405 Do I *need* to implement ``__cmp__`` - I don't *think* so ? 1406 1407 Allow slice assignment to ``OrderedDict`` (and `possibly 1408 ``SequenceOrderedDict.items``) from list of tuples as well as from an 1409 ``OrderedDict`` ? 1410 1411 CHANGELOG 1412 ========= 1413 1414 2005/12/17 1415 ---------- 1416 1417 You can now test for equality and inequality with objects (except for 1418 dictionaries for which it is undefined). This allows you to do tests like : 1419 :: 1420 1421 OrderedDict() == False 1422 1423 Added the ``strict`` keyword, which raises a ``ValueError`` if you do slice 1424 assignment with keys that are already in the dictionary. 1425 1426 Assignment to ``keys`` in ``SequenceOrderedDict`` is now only for 1427 re-ordering the keys. 1428 1429 Fixed bug where slice assignment to ``keys`` could lose information. (and 1430 optimised by slicing ranges to get the indexes we are assigning to instead 1431 of indexing each key). 1432 1433 You change keys, items, and values through new methods ``setkeys``, 1434 ``setitems``, and ``setvalues`` methods. 1435 1436 Minor changes, thanks to Christoph Zwerschke for suggestions. 1437 1438 Added ``__deepcopy__`` method (previously deepcopy failed). 1439 1440 CHanged use of ``slice`` to ``types.SliceType`` for Python 2.2. 1441 1442 0.2.1 1443 1444 1445 2005/12/02 1446 ---------- 1447 1448 Fixed bugs in ``__getattr__`` and ``popitem`` 1449 1450 Optimisation in ``OrderedDict.__init__`` when creating an instance from an 1451 ``OrderedDict`` 1452 1453 Changed ``FancyODict`` to ``SequenceOrderedDict`` 1454 1455 Implemented new ``__repr__`` 1456 1457 0.2.0 1458 1459 2005/12/01 1460 ---------- 1461 1462 Added index to ``OrderedDict.popitem`` 1463 1464 2005/11/30 1465 ---------- 1466 1467 Implemented ``FancyODict``, which has ``keys``, ``items``, ``values`` as 1468 custom, callable, sequence objects. 1469 1470 2005/11/26 1471 ---------- 1472 1473 By Michael Foord - from suggestions on comp.lang.python 1474 1475 Hidden the ``sequence`` attribute 1476 1477 ``items``, ``keys``, ``values`` can now take a list to replace the current 1478 keys, values, or items 1479 1480 Implemented slicing (including deleting a slice and assigning to a slice) 1481 1482 Implemented sequence methods ``sort``, ``reverse``, ``insert``, ``index`` 1483 1484 2005/09/10 1485 ---------- 1486 1487 By Nicola Larosa, based on code from Tim Wegener 1488 <twegener AT radlogic DOT com DOT au> 1489 1490 Create itervalues and iteritems without creating the list up-front 1491 1492 Added doctests for iter methods, and others. 1493 1494 Optimized __setitem__ to be O(1) rather than O(N) 1495 1496 Removed redefined methods that did not alter dict method behaviour, 1497 related doctests moved to the class docstring 1498 1499 Added support for sequences of (key, value) pairs to update 1500 1501 Removed redundant condition from __eq__ 1502 1503 Removed incorrect implementation of __str__ 1504 1505 2005/08/28 1506 ---------- 1507 1508 By Michael Foord 1509 1510 Added __all__ 1511 1512 More than two arguments to ``pop`` now raises an error 1513 1514 Version 0.1.0 finalised 1515 1516 2005/08/13 1517 ---------- 1518 1519 By Nicola Larosa 1520 1521 Added doctests everywhere, fixed much part of implementation 1522 1523 Added comments at top, other doc vars 1524 1525 2005/08/01 1526 ---------- 1527 1528 By Michael Foord 1529 1530 Type tests changed to isinstance 1531 1532 _keys changed to sequence attribute 1533 1534 Allowed creating a dictionary by passing keyword arguments 1535 1536 Shortened __repr__ 1537 1538 Fixed bug in popitem 1539 1540 Other minor changes 1541 """ 1542