2 # An Ordered Dictionary object
3 # Copyright (C) 2005 Nicola Larosa, Michael Foord
4 # E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk
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.
11 # Documentation at http://www.voidspace.org.uk/python/odict.html
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.
17 """A dict that keeps keys in insertion order"""
18 from __future__
import generators
20 __author__
= ('Nicola Larosa <nico-NoSp@m-tekNico.net>,'
21 'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>')
23 __docformat__
= "restructuredtext en"
25 __revision__
= '$Id: odict.py 129 2005-09-12 18:15:28Z teknico $'
29 __all__
= ['OrderedDict', 'SequenceOrderedDict']
32 INTP_VER
= sys
.version_info
[:2]
34 raise RuntimeError("Python v.2.2 or later required")
36 import types
, warnings
38 class OrderedDict(dict):
40 A class of dictionary that keeps the insertion order of keys.
42 All appropriate methods return keys, items, or values in an ordered way.
44 All normal dictionary methods are available. Update and comparison is
45 restricted to other OrderedDict objects.
47 Various sequence methods are available, including the ability to explicitly
48 mutate the key ordering.
52 >>> d = OrderedDict(((1, 3),))
60 >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2]
62 >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4]
63 Traceback (most recent call last):
68 >>> len(OrderedDict())
70 >>> len(OrderedDict(((1, 3), (3, 2), (2, 1))))
75 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
83 OrderedDict([(1, 3), (3, 2), (2, 1)])
87 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
94 def __init__(self
, init_val
=(), strict
=False):
96 Create a new ordered dictionary. Cannot init from a normal dict,
97 nor from kwargs, since items order is undefined in those cases.
99 If the ``strict`` keyword argument is ``True`` (``False`` is the
100 default) then when doing slice assignment - the ``OrderedDict`` you are
101 assigning from *must not* contain any keys in the remaining dict.
105 >>> OrderedDict({1: 1})
106 Traceback (most recent call last):
107 TypeError: undefined order, cannot get items from dict
108 >>> OrderedDict({1: 1}.items())
109 OrderedDict([(1, 1)])
110 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
112 OrderedDict([(1, 3), (3, 2), (2, 1)])
114 OrderedDict([(1, 3), (3, 2), (2, 1)])
118 if isinstance(init_val
, OrderedDict
):
119 self
._sequence
= init_val
.keys()
120 dict.update(self
, init_val
)
121 elif isinstance(init_val
, dict):
122 # we lose compatibility with other ordered dict types this way
123 raise TypeError('undefined order, cannot get items from dict')
126 self
.update(init_val
)
128 ### Special methods ###
130 def __delitem__(self
, key
):
132 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
135 OrderedDict([(1, 3), (2, 1)])
137 Traceback (most recent call last):
141 OrderedDict([(1, 3), (2, 1), (3, 2)])
144 OrderedDict([(2, 1), (3, 2)])
146 if isinstance(key
, types
.SliceType
):
148 keys
= self
._sequence
[key
]
150 dict.__delitem
__(self
, entry
)
151 del self
._sequence
[key
]
153 # do the dict.__delitem__ *first* as it raises
154 # the more appropriate error
155 dict.__delitem
__(self
, key
)
156 self
._sequence
.remove(key
)
158 def __eq__(self
, other
):
160 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
161 >>> d == OrderedDict(d)
163 >>> d == OrderedDict(((1, 3), (2, 1), (3, 2)))
165 >>> d == OrderedDict(((1, 0), (3, 2), (2, 1)))
167 >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
174 if isinstance(other
, OrderedDict
):
176 # Generate both item lists for each compare
177 return (self
.items() == other
.items())
181 def __lt__(self
, other
):
183 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
184 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
190 Traceback (most recent call last):
191 TypeError: Can only compare with other OrderedDicts
193 if not isinstance(other
, OrderedDict
):
194 raise TypeError('Can only compare with other OrderedDicts')
196 # Generate both item lists for each compare
197 return (self
.items() < other
.items())
199 def __le__(self
, other
):
201 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
202 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
203 >>> e = OrderedDict(d)
209 Traceback (most recent call last):
210 TypeError: Can only compare with other OrderedDicts
214 if not isinstance(other
, OrderedDict
):
215 raise TypeError('Can only compare with other OrderedDicts')
217 # Generate both item lists for each compare
218 return (self
.items() <= other
.items())
220 def __ne__(self
, other
):
222 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
223 >>> d != OrderedDict(d)
225 >>> d != OrderedDict(((1, 3), (2, 1), (3, 2)))
227 >>> d != OrderedDict(((1, 0), (3, 2), (2, 1)))
229 >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
236 if isinstance(other
, OrderedDict
):
238 # Generate both item lists for each compare
239 return not (self
.items() == other
.items())
243 def __gt__(self
, other
):
245 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
246 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
252 Traceback (most recent call last):
253 TypeError: Can only compare with other OrderedDicts
255 if not isinstance(other
, OrderedDict
):
256 raise TypeError('Can only compare with other OrderedDicts')
258 # Generate both item lists for each compare
259 return (self
.items() > other
.items())
261 def __ge__(self
, other
):
263 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
264 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
265 >>> e = OrderedDict(d)
271 Traceback (most recent call last):
272 TypeError: Can only compare with other OrderedDicts
276 if not isinstance(other
, OrderedDict
):
277 raise TypeError('Can only compare with other OrderedDicts')
279 # Generate both item lists for each compare
280 return (self
.items() >= other
.items())
284 Used for __repr__ and __str__
286 >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
288 "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])"
289 >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
291 "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])"
292 >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
294 >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
297 return '%s([%s])' % (self
.__class
__.__name
__, ', '.join(
298 ['(%r, %r)' % (key
, self
[key
]) for key
in self
._sequence
]))
300 def __setitem__(self
, key
, val
):
302 Allows slice assignment, so long as the slice is an OrderedDict
303 >>> d = OrderedDict()
308 OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)])
309 >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4)))
311 OrderedDict([(1, 2), (2, 3), (3, 4)])
312 >>> d[::2] = OrderedDict(((7, 8), (9, 10)))
314 OrderedDict([(7, 8), (2, 3), (9, 10)])
315 >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)))
316 >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
318 OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
319 >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True)
320 >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
322 OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
324 >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True)
327 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
328 >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
330 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
331 >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)])
332 Traceback (most recent call last):
333 ValueError: slice assignment must be from unique keys
334 >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)))
337 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
338 >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
340 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
341 >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
343 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
344 >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
346 OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)])
348 >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
350 Traceback (most recent call last):
351 TypeError: slice assignment requires an OrderedDict
353 >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
354 >>> d[:1] = OrderedDict([(9, 8)])
356 OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)])
358 if isinstance(key
, types
.SliceType
):
359 if not isinstance(val
, OrderedDict
):
360 # FIXME: allow a list of tuples?
361 raise TypeError('slice assignment requires an OrderedDict')
362 keys
= self
._sequence
[key
]
363 # NOTE: Could use ``range(*key.indices(len(self._sequence)))``
364 indexes
= range(len(self
._sequence
))[key
]
366 # NOTE: new slice may not be the same size as the one being
368 # NOTE: What is the algorithm for an impossible slice?
376 raise ValueError('slice assignment must be from '
379 # NOTE: This removes duplicate keys *first*
380 # so start position might have changed?
382 self
._sequence
= (self
._sequence
[:pos
] + newkeys
+
383 self
._sequence
[pos
:])
384 dict.update(self
, val
)
386 # extended slice - length of new slice must be the same
387 # as the one being replaced
388 if len(keys
) != len(val
):
389 raise ValueError('attempt to assign sequence of size %s '
390 'to extended slice of size %s' % (len(val
), len(keys
)))
393 item_list
= zip(indexes
, val
.items())
394 # smallest indexes first - higher indexes not guaranteed to
397 for pos
, (newkey
, newval
) in item_list
:
398 if self
.strict
and newkey
in self
:
399 raise ValueError('slice assignment must be from unique'
401 self
.insert(pos
, newkey
, newval
)
404 self
._sequence
.append(key
)
405 dict.__setitem
__(self
, key
, val
)
407 def __getitem__(self
, key
):
409 Allows slicing. Returns an OrderedDict if you slice.
410 >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)])
412 OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)])
414 OrderedDict([(5, 2), (4, 3), (3, 4)])
416 <class '__main__.OrderedDict'>
418 if isinstance(key
, types
.SliceType
):
419 # FIXME: does this raise the error we want?
420 keys
= self
._sequence
[key
]
422 return OrderedDict([(entry
, self
[entry
]) for entry
in keys
])
424 return dict.__getitem
__(self
, key
)
428 def __setattr__(self
, name
, value
):
430 Implemented so that accesses to ``sequence`` raise a warning and are
431 diverted to the new ``setkeys`` method.
433 if name
== 'sequence':
434 warnings
.warn('Use of the sequence attribute is deprecated.'
435 ' Use the keys method instead.', DeprecationWarning)
436 # NOTE: doesn't return anything
439 # FIXME: do we want to allow arbitrary setting of attributes?
440 # Or do we want to manage it?
441 object.__setattr
__(self
, name
, value
)
443 def __getattr__(self
, name
):
445 Implemented so that access to ``sequence`` raises a warning.
447 >>> d = OrderedDict()
451 if name
== 'sequence':
452 warnings
.warn('Use of the sequence attribute is deprecated.'
453 ' Use the keys method instead.', DeprecationWarning)
454 # NOTE: Still (currently) returns a direct reference. Need to
455 # because code that uses sequence will expect to be able to
456 # mutate it in place.
457 return self
._sequence
459 # raise the appropriate error
460 raise AttributeError("OrderedDict has no '%s' attribute" % name
)
462 def __deepcopy__(self
, memo
):
464 To allow deepcopy to work with OrderedDict.
466 >>> from copy import deepcopy
467 >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)])
474 >>> a['test'] is b['test']
477 from copy
import deepcopy
478 return self
.__class
__(deepcopy(self
.items(), memo
), self
.strict
)
481 ### Read-only methods ###
485 >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy()
486 OrderedDict([(1, 3), (3, 2), (2, 1)])
488 return OrderedDict(self
)
492 ``items`` returns a list of tuples representing all the
493 ``(key, value)`` pairs in the dictionary.
495 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
497 [(1, 3), (3, 2), (2, 1)]
502 return zip(self
._sequence
, self
.values())
506 Return a list of keys in the ``OrderedDict``.
508 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
512 return self
._sequence
[:]
514 def values(self
, values
=None):
516 Return a list of all the values in the OrderedDict.
518 Optionally you can pass in a list of values, which will replace the
519 current list. The value list must be the same len as the OrderedDict.
521 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
525 return [self
[key
] for key
in self
._sequence
]
529 >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems()
537 Traceback (most recent call last):
540 def make_iter(self
=self
):
541 keys
= self
.iterkeys()
544 yield (key
, self
[key
])
549 >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys()
557 Traceback (most recent call last):
560 return iter(self
._sequence
)
564 def itervalues(self
):
566 >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues()
574 Traceback (most recent call last):
577 def make_iter(self
=self
):
578 keys
= self
.iterkeys()
580 yield self
[keys
.next()]
583 ### Read-write methods ###
587 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
595 def pop(self
, key
, *args
):
597 No dict.pop in Python 2.2, gotta reimplement it
599 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
603 OrderedDict([(1, 3), (2, 1)])
605 Traceback (most recent call last):
610 Traceback (most recent call last):
611 TypeError: pop expected at most 2 arguments, got 3
614 raise TypeError, ('pop expected at most 2 arguments, got %s' %
626 def popitem(self
, i
=-1):
628 Delete and return an item specified by index, not a random one as in
629 dict. The index is -1 by default (the last item).
631 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
635 OrderedDict([(1, 3), (3, 2)])
638 >>> OrderedDict().popitem()
639 Traceback (most recent call last):
640 KeyError: 'popitem(): dictionary is empty'
642 Traceback (most recent call last):
643 IndexError: popitem(): index 2 not valid
645 if not self
._sequence
:
646 raise KeyError('popitem(): dictionary is empty')
648 key
= self
._sequence
[i
]
650 raise IndexError('popitem(): index %s not valid' % i
)
651 return (key
, self
.pop(key
))
653 def setdefault(self
, key
, defval
= None):
655 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
658 >>> d.setdefault(4) is None
661 OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)])
662 >>> d.setdefault(5, 0)
665 OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)])
673 def update(self
, from_od
):
675 Update from another OrderedDict or sequence of (key, value) pairs
677 >>> d = OrderedDict(((1, 0), (0, 1)))
678 >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1))))
680 OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)])
682 Traceback (most recent call last):
683 TypeError: undefined order, cannot get items from dict
685 Traceback (most recent call last):
686 TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence
688 if isinstance(from_od
, OrderedDict
):
689 for key
, val
in from_od
.items():
691 elif isinstance(from_od
, dict):
692 # we lose compatibility with other ordered dict types this way
693 raise TypeError('undefined order, cannot get items from dict')
696 # sequence of 2-item sequences, or error
701 raise TypeError('cannot convert dictionary update'
702 ' sequence element "%s" to a 2-item sequence' % item
)
705 def rename(self
, old_key
, new_key
):
707 Rename the key for a given value, without modifying sequence order.
709 For the case where new_key already exists this raise an exception,
710 since if new_key exists, it is ambiguous as to what happens to the
711 associated values, and the position of new_key in the sequence.
713 >>> od = OrderedDict()
718 >>> od.rename('b', 'c')
721 >>> od.rename('c', 'a')
722 Traceback (most recent call last):
723 ValueError: New key already exists: 'a'
724 >>> od.rename('d', 'b')
725 Traceback (most recent call last):
728 if new_key
== old_key
:
732 raise ValueError("New key already exists: %r" % new_key
)
733 # rename sequence entry
734 value
= self
[old_key
]
735 old_idx
= self
._sequence
.index(old_key
)
736 self
._sequence
[old_idx
] = new_key
737 # rename internal dict entry
738 dict.__delitem
__(self
, old_key
)
739 dict.__setitem
__(self
, new_key
, value
)
741 def setitems(self
, items
):
743 This method allows you to set the items in the dict.
745 It takes a list of tuples - of the same sort returned by the ``items``
748 >>> d = OrderedDict()
749 >>> d.setitems(((3, 1), (2, 3), (1, 2)))
751 OrderedDict([(3, 1), (2, 3), (1, 2)])
754 # FIXME: this allows you to pass in an OrderedDict as well :-)
757 def setkeys(self
, keys
):
759 ``setkeys`` all ows you to pass in a new list of keys which will
760 replace the current set. This must contain the same set of keys, but
761 need not be in the same order.
763 If you pass in new keys that don't match, a ``KeyError`` will be
766 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
769 >>> d.setkeys((1, 2, 3))
771 OrderedDict([(1, 3), (2, 1), (3, 2)])
772 >>> d.setkeys(['a', 'b', 'c'])
773 Traceback (most recent call last):
774 KeyError: 'Keylist is not the same as current keylist.'
776 # FIXME: Efficiency? (use set for Python 2.4 :-)
777 # NOTE: list(keys) rather than keys[:] because keys[:] returns
778 # a tuple, if keys is a tuple.
781 self
._sequence
.sort()
782 if kcopy
!= self
._sequence
:
783 raise KeyError('Keylist is not the same as current keylist.')
784 # NOTE: This makes the _sequence attribute a new object, instead
785 # of changing it in place.
787 self
._sequence
= list(keys
)
789 def setvalues(self
, values
):
791 You can pass in a list of values, which will replace the
792 current list. The value list must be the same len as the OrderedDict.
794 (Or a ``ValueError`` is raised.)
796 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
797 >>> d.setvalues((1, 2, 3))
799 OrderedDict([(1, 1), (3, 2), (2, 3)])
801 Traceback (most recent call last):
802 ValueError: Value list is not the same length as the OrderedDict.
804 if len(values
) != len(self
):
805 # FIXME: correct error to raise?
806 raise ValueError('Value list is not the same length as the '
808 self
.update(zip(self
, values
))
810 ### Sequence Methods ###
812 def index(self
, key
):
814 Return the position of the specified key in the OrderedDict.
816 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
820 Traceback (most recent call last):
821 ValueError: list.index(x): x not in list
823 return self
._sequence
.index(key
)
825 def insert(self
, index
, key
, value
):
827 Takes ``index``, ``key``, and ``value`` as arguments.
829 Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in
832 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
833 >>> d.insert(0, 4, 0)
835 OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)])
836 >>> d.insert(0, 2, 1)
838 OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)])
839 >>> d.insert(8, 8, 1)
841 OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)])
846 self
._sequence
.insert(index
, key
)
847 dict.__setitem
__(self
, key
, value
)
851 Reverse the order of the OrderedDict.
853 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
856 OrderedDict([(2, 1), (3, 2), (1, 3)])
858 self
._sequence
.reverse()
860 def sort(self
, *args
, **kwargs
):
862 Sort the key order in the OrderedDict.
864 This method takes the same arguments as the ``list.sort`` method on
865 your version of Python.
867 >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4)))
870 OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)])
872 self
._sequence
.sort(*args
, **kwargs
)
875 # FIXME: should this object be a subclass of list?
877 Custom object for accessing the keys of an OrderedDict.
879 Can be called like the normal ``OrderedDict.keys`` method, but also
880 supports indexing and sequence methods.
883 def __init__(self
, main
):
887 """Pretend to be the keys method."""
888 return self
._main
._keys
()
890 def __getitem__(self
, index
):
891 """Fetch the key at position i."""
892 # NOTE: this automatically supports slicing :-)
893 return self
._main
._sequence
[index
]
895 def __setitem__(self
, index
, name
):
897 You cannot assign to keys, but you can do slice assignment to re-order
900 You can only do slice assignment if the new set of keys is a reordering
903 if isinstance(index
, types
.SliceType
):
905 # check length is the same
906 indexes
= range(len(self
._main
._sequence
))[index
]
907 if len(indexes
) != len(name
):
908 raise ValueError('attempt to assign sequence of size %s '
909 'to slice of size %s' % (len(name
), len(indexes
)))
910 # check they are the same keys
912 old_keys
= self
._main
._sequence
[index
]
913 new_keys
= list(name
)
916 if old_keys
!= new_keys
:
917 raise KeyError('Keylist is not the same as current keylist.')
918 orig_vals
= [self
._main
[k
] for k
in name
]
919 del self
._main
[index
]
920 vals
= zip(indexes
, name
, orig_vals
)
923 if self
._main
.strict
and k
in self
._main
:
924 raise ValueError('slice assignment must be from '
926 self
._main
.insert(i
, k
, v
)
928 raise ValueError('Cannot assign to keys')
930 ### following methods pinched from UserList and adapted ###
931 def __repr__(self
): return repr(self
._main
._sequence
)
933 # FIXME: do we need to check if we are comparing with another ``Keys``
934 # object? (like the __cast method of UserList)
935 def __lt__(self
, other
): return self
._main
._sequence
< other
936 def __le__(self
, other
): return self
._main
._sequence
<= other
937 def __eq__(self
, other
): return self
._main
._sequence
== other
938 def __ne__(self
, other
): return self
._main
._sequence
!= other
939 def __gt__(self
, other
): return self
._main
._sequence
> other
940 def __ge__(self
, other
): return self
._main
._sequence
>= other
941 # FIXME: do we need __cmp__ as well as rich comparisons?
942 def __cmp__(self
, other
): return cmp(self
._main
._sequence
, other
)
944 def __contains__(self
, item
): return item
in self
._main
._sequence
945 def __len__(self
): return len(self
._main
._sequence
)
946 def __iter__(self
): return self
._main
.iterkeys()
947 def count(self
, item
): return self
._main
._sequence
.count(item
)
948 def index(self
, item
, *args
): return self
._main
._sequence
.index(item
, *args
)
949 def reverse(self
): self
._main
._sequence
.reverse()
950 def sort(self
, *args
, **kwds
): self
._main
._sequence
.sort(*args
, **kwds
)
951 def __mul__(self
, n
): return self
._main
._sequence
*n
953 def __add__(self
, other
): return self
._main
._sequence
+ other
954 def __radd__(self
, other
): return other
+ self
._main
._sequence
956 ## following methods not implemented for keys ##
957 def __delitem__(self
, i
): raise TypeError('Can\'t delete items from keys')
958 def __iadd__(self
, other
): raise TypeError('Can\'t add in place to keys')
959 def __imul__(self
, n
): raise TypeError('Can\'t multiply keys in place')
960 def append(self
, item
): raise TypeError('Can\'t append items to keys')
961 def insert(self
, i
, item
): raise TypeError('Can\'t insert items into keys')
962 def pop(self
, i
=-1): raise TypeError('Can\'t pop items from keys')
963 def remove(self
, item
): raise TypeError('Can\'t remove items from keys')
964 def extend(self
, other
): raise TypeError('Can\'t extend keys')
968 Custom object for accessing the items of an OrderedDict.
970 Can be called like the normal ``OrderedDict.items`` method, but also
971 supports indexing and sequence methods.
974 def __init__(self
, main
):
978 """Pretend to be the items method."""
979 return self
._main
._items
()
981 def __getitem__(self
, index
):
982 """Fetch the item at position i."""
983 if isinstance(index
, types
.SliceType
):
984 # fetching a slice returns an OrderedDict
985 return self
._main
[index
].items()
986 key
= self
._main
._sequence
[index
]
987 return (key
, self
._main
[key
])
989 def __setitem__(self
, index
, item
):
990 """Set item at position i to item."""
991 if isinstance(index
, types
.SliceType
):
992 # NOTE: item must be an iterable (list of tuples)
993 self
._main
[index
] = OrderedDict(item
)
995 # FIXME: Does this raise a sensible error?
996 orig
= self
._main
.keys
[index
]
998 if self
._main
.strict
and key
in self
and (key
!= orig
):
999 raise ValueError('slice assignment must be from '
1001 # delete the current one
1002 del self
._main
[self
._main
._sequence
[index
]]
1003 self
._main
.insert(index
, key
, value
)
1005 def __delitem__(self
, i
):
1006 """Delete the item at position i."""
1007 key
= self
._main
._sequence
[i
]
1008 if isinstance(i
, types
.SliceType
):
1010 # FIXME: efficiency?
1015 ### following methods pinched from UserList and adapted ###
1016 def __repr__(self
): return repr(self
._main
.items())
1018 # FIXME: do we need to check if we are comparing with another ``Items``
1019 # object? (like the __cast method of UserList)
1020 def __lt__(self
, other
): return self
._main
.items() < other
1021 def __le__(self
, other
): return self
._main
.items() <= other
1022 def __eq__(self
, other
): return self
._main
.items() == other
1023 def __ne__(self
, other
): return self
._main
.items() != other
1024 def __gt__(self
, other
): return self
._main
.items() > other
1025 def __ge__(self
, other
): return self
._main
.items() >= other
1026 def __cmp__(self
, other
): return cmp(self
._main
.items(), other
)
1028 def __contains__(self
, item
): return item
in self
._main
.items()
1029 def __len__(self
): return len(self
._main
._sequence
) # easier :-)
1030 def __iter__(self
): return self
._main
.iteritems()
1031 def count(self
, item
): return self
._main
.items().count(item
)
1032 def index(self
, item
, *args
): return self
._main
.items().index(item
, *args
)
1033 def reverse(self
): self
._main
.reverse()
1034 def sort(self
, *args
, **kwds
): self
._main
.sort(*args
, **kwds
)
1035 def __mul__(self
, n
): return self
._main
.items()*n
1037 def __add__(self
, other
): return self
._main
.items() + other
1038 def __radd__(self
, other
): return other
+ self
._main
.items()
1040 def append(self
, item
):
1041 """Add an item to the end."""
1042 # FIXME: this is only append if the key isn't already present
1044 self
._main
[key
] = value
1046 def insert(self
, i
, item
):
1048 self
._main
.insert(i
, key
, value
)
1050 def pop(self
, i
=-1):
1051 key
= self
._main
._sequence
[i
]
1052 return (key
, self
._main
.pop(key
))
1054 def remove(self
, item
):
1057 assert value
== self
._main
[key
]
1058 except (KeyError, AssertionError):
1059 raise ValueError('ValueError: list.remove(x): x not in list')
1063 def extend(self
, other
):
1064 # FIXME: is only a true extend if none of the keys already present
1067 self
._main
[key
] = value
1069 def __iadd__(self
, other
):
1072 ## following methods not implemented for items ##
1074 def __imul__(self
, n
): raise TypeError('Can\'t multiply items in place')
1076 class Values(object):
1078 Custom object for accessing the values of an OrderedDict.
1080 Can be called like the normal ``OrderedDict.values`` method, but also
1081 supports indexing and sequence methods.
1084 def __init__(self
, main
):
1088 """Pretend to be the values method."""
1089 return self
._main
._values
()
1091 def __getitem__(self
, index
):
1092 """Fetch the value at position i."""
1093 if isinstance(index
, types
.SliceType
):
1094 return [self
._main
[key
] for key
in self
._main
._sequence
[index
]]
1096 return self
._main
[self
._main
._sequence
[index
]]
1098 def __setitem__(self
, index
, value
):
1100 Set the value at position i to value.
1102 You can only do slice assignment to values if you supply a sequence of
1103 equal length to the slice you are replacing.
1105 if isinstance(index
, types
.SliceType
):
1106 keys
= self
._main
._sequence
[index
]
1107 if len(keys
) != len(value
):
1108 raise ValueError('attempt to assign sequence of size %s '
1109 'to slice of size %s' % (len(name
), len(keys
)))
1110 # FIXME: efficiency? Would be better to calculate the indexes
1111 # directly from the slice object
1112 # NOTE: the new keys can collide with existing keys (or even
1113 # contain duplicates) - these will overwrite
1114 for key
, val
in zip(keys
, value
):
1115 self
._main
[key
] = val
1117 self
._main
[self
._main
._sequence
[index
]] = value
1119 ### following methods pinched from UserList and adapted ###
1120 def __repr__(self
): return repr(self
._main
.values())
1122 # FIXME: do we need to check if we are comparing with another ``Values``
1123 # object? (like the __cast method of UserList)
1124 def __lt__(self
, other
): return self
._main
.values() < other
1125 def __le__(self
, other
): return self
._main
.values() <= other
1126 def __eq__(self
, other
): return self
._main
.values() == other
1127 def __ne__(self
, other
): return self
._main
.values() != other
1128 def __gt__(self
, other
): return self
._main
.values() > other
1129 def __ge__(self
, other
): return self
._main
.values() >= other
1130 def __cmp__(self
, other
): return cmp(self
._main
.values(), other
)
1132 def __contains__(self
, item
): return item
in self
._main
.values()
1133 def __len__(self
): return len(self
._main
._sequence
) # easier :-)
1134 def __iter__(self
): return self
._main
.itervalues()
1135 def count(self
, item
): return self
._main
.values().count(item
)
1136 def index(self
, item
, *args
): return self
._main
.values().index(item
, *args
)
1139 """Reverse the values"""
1140 vals
= self
._main
.values()
1145 def sort(self
, *args
, **kwds
):
1146 """Sort the values."""
1147 vals
= self
._main
.values()
1148 vals
.sort(*args
, **kwds
)
1151 def __mul__(self
, n
): return self
._main
.values()*n
1153 def __add__(self
, other
): return self
._main
.values() + other
1154 def __radd__(self
, other
): return other
+ self
._main
.values()
1156 ## following methods not implemented for values ##
1157 def __delitem__(self
, i
): raise TypeError('Can\'t delete items from values')
1158 def __iadd__(self
, other
): raise TypeError('Can\'t add in place to values')
1159 def __imul__(self
, n
): raise TypeError('Can\'t multiply values in place')
1160 def append(self
, item
): raise TypeError('Can\'t append items to values')
1161 def insert(self
, i
, item
): raise TypeError('Can\'t insert items into values')
1162 def pop(self
, i
=-1): raise TypeError('Can\'t pop items from values')
1163 def remove(self
, item
): raise TypeError('Can\'t remove items from values')
1164 def extend(self
, other
): raise TypeError('Can\'t extend values')
1166 class SequenceOrderedDict(OrderedDict
):
1168 Experimental version of OrderedDict that has a custom object for ``keys``,
1169 ``values``, and ``items``.
1171 These are callable sequence objects that work as methods, or can be
1172 manipulated directly as sequences.
1174 Test for ``keys``, ``items`` and ``values``.
1176 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
1178 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
1183 >>> d.setkeys((3, 2, 1))
1185 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
1186 >>> d.setkeys((1, 2, 3))
1195 >>> d.keys[0:2] = [2, 1]
1197 SequenceOrderedDict([(2, 3), (1, 2), (3, 4)])
1198 >>> d.keys.reverse()
1201 >>> d.keys = [1, 2, 3]
1203 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
1204 >>> d.keys = [3, 1, 2]
1206 SequenceOrderedDict([(3, 4), (1, 2), (2, 3)])
1207 >>> a = SequenceOrderedDict()
1208 >>> b = SequenceOrderedDict()
1209 >>> a.keys == b.keys
1212 >>> a.keys == b.keys
1215 >>> a.keys == b.keys
1218 >>> a.keys == b.keys
1232 >>> [v for v in d.keys]
1237 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True)
1238 >>> d.keys[::-1] = [1, 2, 3]
1240 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
1243 >>> d.keys[:2] = [1, 3]
1244 Traceback (most recent call last):
1245 KeyError: 'Keylist is not the same as current keylist.'
1247 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
1249 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
1254 >>> d.setvalues((4, 3, 2))
1256 SequenceOrderedDict([(1, 4), (2, 3), (3, 2)])
1264 Traceback (most recent call last):
1265 TypeError: Can't delete items from values
1266 >>> d.values[::2] = [2, 4]
1268 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
1273 >>> [val for val in d.values]
1275 >>> d.values[-1] = 2
1276 >>> d.values.count(2)
1278 >>> d.values.index(2)
1280 >>> d.values[-1] = 7
1283 >>> d.values.reverse()
1289 >>> d.values.append('anything')
1290 Traceback (most recent call last):
1291 TypeError: Can't append items to values
1292 >>> d.values = (1, 2, 3)
1294 SequenceOrderedDict([(1, 1), (2, 2), (3, 3)])
1296 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
1298 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
1300 [(1, 2), (2, 3), (3, 4)]
1301 >>> d.setitems([(3, 4), (2 ,3), (1, 2)])
1303 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
1308 >>> d.items[1] = (6, 3)
1310 [(3, 4), (6, 3), (1, 2)]
1311 >>> d.items[1:2] = [(9, 9)]
1313 SequenceOrderedDict([(3, 4), (9, 9), (1, 2)])
1314 >>> del d.items[1:2]
1316 SequenceOrderedDict([(3, 4), (1, 2)])
1317 >>> (3, 4) in d.items
1319 >>> (4, 3) in d.items
1323 >>> [v for v in d.items]
1325 >>> d.items.count((3, 4))
1327 >>> d.items.index((1, 2))
1329 >>> d.items.index((2, 1))
1330 Traceback (most recent call last):
1331 ValueError: list.index(x): x not in list
1332 >>> d.items.reverse()
1335 >>> d.items.reverse()
1339 >>> d.items.append((5, 6))
1341 [(1, 2), (3, 4), (5, 6)]
1342 >>> d.items.insert(0, (0, 0))
1344 [(0, 0), (1, 2), (3, 4), (5, 6)]
1345 >>> d.items.insert(-1, (7, 8))
1347 [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)]
1351 [(0, 0), (1, 2), (3, 4), (7, 8)]
1352 >>> d.items.remove((1, 2))
1354 [(0, 0), (3, 4), (7, 8)]
1355 >>> d.items.extend([(1, 2), (5, 6)])
1357 [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)]
1360 def __init__(self
, init_val
=(), strict
=True):
1361 OrderedDict
.__init
__(self
, init_val
, strict
=strict
)
1362 self
._keys
= self
.keys
1363 self
._values
= self
.values
1364 self
._items
= self
.items
1365 self
.keys
= Keys(self
)
1366 self
.values
= Values(self
)
1367 self
.items
= Items(self
)
1369 'keys': self
.setkeys
,
1370 'items': self
.setitems
,
1371 'values': self
.setvalues
,
1374 def __setattr__(self
, name
, value
):
1375 """Protect keys, items, and values."""
1376 if not '_att_dict' in self
.__dict
__:
1377 object.__setattr
__(self
, name
, value
)
1380 fun
= self
._att
_dict
[name
]
1382 OrderedDict
.__setattr
__(self
, name
, value
)
1386 if __name__
== '__main__':
1387 if INTP_VER
< (2, 3):
1388 raise RuntimeError("Tests require Python v.2.3 or later")
1389 # turn off warnings for tests
1390 warnings
.filterwarnings('ignore')
1391 # run the code tests in doctest format
1393 m
= sys
.modules
.get('__main__')
1394 globs
= m
.__dict
__.copy()
1396 'INTP_VER': INTP_VER
,
1398 doctest
.testmod(m
, globs
=globs
)