Since an OrderedDict
has the features of both a list (with ordered elements), and a dictionary (with keys instead of indexes), it would seem natural that you could slice using keys.
>>> from collections import OrderedDict >>> cities = OrderedDict((('san francisco', 650), ('new york', 212), ('shanghai', 8621), ('barcelona', 42423))) >>> test['shanghai':] # I want all the cities from shanghai to the end of the list TypeError: unhashable type
What’s interesting about this is that it’s not the error you’d see due to OrderedDictionary.__getslice__
not being implemented. I tried adding my own __getslice__
method to OrderedDict
, but I keep running into this TypeError problem. It seems like Python is doing some kind of type checking to enforce that slice keys are only integers, before they even get passed to the __getslice__
function, how unpythonic!
>>> class BetterOrderedDict(OrderedDict): def __getslice__(self, start=None, end=None, step=1): return 'potato' >>> test = BetterOrderedDict((('one', 1), ('two', 2), ('three', 3), ('four', 4))) >>> print test[1:4] 'potato' # ok this makes sense so far >>> test['one':'four'] TypeError: unhashable type # WTF, strings are hashable!
So my question is, why can’t I implement non-int slices, what kind of type-checking is preventing the slice keys from even reaching my __getslice__
function, and can I override it by implementing my BetterOrderedDict
in C with bindings?
Advertisement
Answer
__getslice__
is deprecated way of implementing slicing. Instead you should handle slice
objects with __getitem__
:
from collections import OrderedDict class SlicableDict(OrderedDict): def __getitem__(self, key): if isinstance(key, slice): return 'potato({},{},{})'.format(key.start, key.stop, key.step) return super(SlicableDict, self).__getitem__(key) >>> s = SlicableDict(a=1, b=2, c=3) >>> s SlicableDict([('a', 1), ('c', 3), ('b', 2)]) >>> s['a'] 1 >>> s['a':'c'] 'potato(a,c,None)'
And if you need more than potato, than you can implement all three slicing operations following way:
def _key_slice_to_index_slice(items, key_slice): try: if key_slice.start is None: start = None else: start = next(idx for idx, (key, value) in enumerate(items) if key == key_slice.start) if key_slice.stop is None: stop = None else: stop = next(idx for idx, (key, value) in enumerate(items) if key == key_slice.stop) except StopIteration: raise KeyError return slice(start, stop, key_slice.step) class SlicableDict(OrderedDict): def __getitem__(self, key): if isinstance(key, slice): items = self.items() index_slice = _key_slice_to_index_slice(items, key) return SlicableDict(items[index_slice]) return super(SlicableDict, self).__getitem__(key) def __setitem__(self, key, value): if isinstance(key, slice): items = self.items() index_slice = _key_slice_to_index_slice(items, key) items[index_slice] = value.items() self.clear() self.update(items) return return super(SlicableDict, self).__setitem__(key, value) def __delitem__(self, key): if isinstance(key, slice): items = self.items() index_slice = _key_slice_to_index_slice(items, key) del items[index_slice] self.clear() self.update(items) return return super(SlicableDict, self).__delitem__(key)