| # Copyright (c) 2006-2009 Nathan Binkert <nate@binkert.org> |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer; |
| # redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution; |
| # neither the name of the copyright holders nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| from __future__ import print_function |
| from __future__ import absolute_import |
| |
| from bisect import bisect_left, bisect_right |
| |
| class SortedDict(dict): |
| def _get_sorted(self): |
| return getattr(self, '_sorted', sorted) |
| def _set_sorted(self, val): |
| self._sorted = val |
| self._del_keys() |
| sorted = property(_get_sorted, _set_sorted) |
| |
| @property |
| def _keys(self): |
| try: |
| return self._sorted_keys |
| except AttributeError: |
| _sorted_keys = self.sorted(dict.keys(self)) |
| self._sorted_keys = _sorted_keys |
| return _sorted_keys |
| |
| def _left_eq(self, key): |
| index = self._left_ge(self, key) |
| if self._keys[index] != key: |
| raise KeyError(key) |
| return index |
| |
| def _right_eq(self, key): |
| index = self._right_le(self, key) |
| if self._keys[index] != key: |
| raise KeyError(key) |
| return index |
| |
| def _right_lt(self, key): |
| index = bisect_left(self._keys, key) |
| if index: |
| return index - 1 |
| raise KeyError(key) |
| |
| def _right_le(self, key): |
| index = bisect_right(self._keys, key) |
| if index: |
| return index - 1 |
| raise KeyError(key) |
| |
| def _left_gt(self, key): |
| index = bisect_right(self._keys, key) |
| if index != len(self._keys): |
| return index |
| raise KeyError(key) |
| |
| def _left_ge(self, key): |
| index = bisect_left(self._keys, key) |
| if index != len(self._keys): |
| return index |
| raise KeyError(key) |
| |
| def _del_keys(self): |
| try: |
| del self._sorted_keys |
| except AttributeError: |
| pass |
| |
| def __repr__(self): |
| return 'SortedDict({%s})' % ', '.join('%r: %r' % item |
| for item in self.items()) |
| def __setitem__(self, key, item): |
| dict.__setitem__(self, key, item) |
| self._del_keys() |
| |
| def __delitem__(self, key): |
| dict.__delitem__(self, key) |
| self._del_keys() |
| |
| def clear(self): |
| self.data.clear() |
| self._del_keys() |
| |
| def copy(self): |
| t = type(self) |
| return t(self) |
| |
| def keys(self): |
| return self._keys |
| |
| def values(self): |
| for k in self._keys: |
| yield self[k] |
| |
| def items(self): |
| for k in self._keys: |
| yield k, self[k] |
| |
| def keyrange(self, start=None, end=None, inclusive=False): |
| if start is not None: |
| start = self._left_ge(start) |
| |
| if end is not None: |
| if inclusive: |
| end = self._right_le(end) |
| else: |
| end = self._right_lt(end) |
| |
| return iter(self._keys[start:end+1]) |
| |
| def valuerange(self, *args, **kwargs): |
| for k in self.keyrange(*args, **kwargs): |
| yield self[k] |
| |
| def itemrange(self, *args, **kwargs): |
| for k in self.keyrange(*args, **kwargs): |
| yield k, self[k] |
| |
| def update(self, *args, **kwargs): |
| dict.update(self, *args, **kwargs) |
| self._del_keys() |
| |
| def setdefault(self, key, _failobj=None): |
| try: |
| return self[key] |
| except KeyError: |
| self[key] = _failobj |
| |
| def pop(self, key, *args): |
| try: |
| dict.pop(self, key) |
| self._del_keys() |
| except KeyError: |
| if not args: |
| raise |
| return args[0] |
| |
| def popitem(self): |
| try: |
| key = self._keys[0] |
| self._del_keys() |
| except IndexError: |
| raise KeyError('popitem(): dictionary is empty') |
| else: |
| return key, dict.pop(self, key) |
| |
| @classmethod |
| def fromkeys(cls, seq, value=None): |
| d = cls() |
| for key in seq: |
| d[key] = value |
| return d |
| |
| if __name__ == '__main__': |
| def display(d): |
| print(d) |
| print(list(d.keys())) |
| print(list(d.keys())) |
| print(list(d.values())) |
| print(list(d.values())) |
| print(list(d.items())) |
| print(list(d.items())) |
| |
| d = SortedDict(x=24,e=5,j=4,b=2,z=26,d=4) |
| display(d) |
| |
| print('popitem', d.popitem()) |
| display(d) |
| |
| print('pop j') |
| d.pop('j') |
| display(d) |
| |
| d.setdefault('a', 1) |
| d.setdefault('g', 7) |
| d.setdefault('_') |
| display(d) |
| |
| d.update({'b' : 2, 'h' : 8}) |
| display(d) |
| |
| del d['x'] |
| display(d) |
| d['y'] = 26 |
| display(d) |
| |
| print(repr(d)) |
| |
| print(d.copy()) |
| |
| for k,v in d.itemrange('d', 'z', inclusive=True): |
| print(k, v) |