Disclaimer: This answer is mainly explanatory to show one (out of many) things that Python allows you to do. Also, you should probably NOT want to do things this way (the fact that you can do things doesn't mean you should)
This said, the documentation for getitem(self, key) states:
For sequence types, the accepted keys should be integers and slice
objects.
which means that any class that wants to mimic the behavior of a sequence type (such as a list) must be prepared to implement a __getitem__
where key can be a type slice. That (kind of) explains (or at least, is somehow related to) why in your dictionary you're getting a TypeError: unhashable type
when you try to do b[slice(st,en)] = 'bla '
: It's because it's trying to use the slice(st, en)
instance as a dictionary key. slice
objects are not hashable, and therefore, can't be used as dict
keys. A dict
type is NOT a sequence type, so trying to slice a dictionary has no meaning.
Let's say you have:
{
... 'foo': 1,
... 'bar': 2,
... 'baz': 3,
... }
What does it mean an slice
from 'foo'
to 'bar'
? Would you return the set of keys in the order you entered them? ('foo', 'bar', 'baz')? Python doesn't know about that. Would it be their __hash__
? That's internal, meaningless when it comes so slicing.
All this said, here's a very very bad thing... but that "works":
import datetime
class DatetimeDict(dict):
def __getitem__(self, key):
if isinstance(key, slice):
sliced = {}
start_dt = key.start
stop_dt = key.stop
step = key.step or 1
internal_keys = sorted(self.keys())
if start_dt is None:
start_index = 0
else:
start_index = internal_keys.index(start_dt)
end_index = internal_keys.index(stop_dt)
for i in range(start_index, end_index, step):
sliced.update({internal_keys[i]: self[internal_keys[i]]})
return sliced
else:
return super(DatetimeDict, self).__getitem__(key)
def __setitem__(self, key, val):
return super(DatetimeDict, self).__setitem__(key, val)
a = DatetimeDict()
a[datetime.datetime.strptime('2014/01/01', '%Y/%m/%d')] = 'foo',
a[datetime.datetime.strptime('2014/01/02', '%Y/%m/%d')] = 'bar',
a[datetime.datetime.strptime('2014/01/03', '%Y/%m/%d')] = 'baz',
a[datetime.datetime.strptime('2014/01/04', '%Y/%m/%d')] = 'bla',
from_dt = datetime.datetime.strptime('2014/01/02', '%Y/%m/%d')
to_dt = datetime.datetime.strptime('2014/01/04', '%Y/%m/%d')
print a[from_dt:to_dt]
This outputs:
{
datetime.datetime(2014, 1, 2, 0, 0): ('bar',),
datetime.datetime(2014, 1, 3, 0, 0): ('baz',)
}
But is bad, bad bad... DatetimeDict
becomes a weird construct that is a dictionary but at the same time behaves kind of like a sequence type... Bad.
EDIT (After re-reading, I'm pretty sure I misunderstood the question)
You weren't actually trying to slice a dict
, where you? The day I learn to read, I'll conquer the world... :-D
If what you want is use a range of datetimes as a dict
key, I'd recommend that you just put the start
and end
in a tuple
:
>>> import datetime
>>> st = datetime.datetime.strptime('2014/01/01', '%Y/%m/%d')
>>> en = datetime.datetime.strptime('2014/01/02', '%Y/%m/%d')
>>> key = (st, en)
>>> a = {}
>>> a[key] = 'foo'
>>> print a
{(datetime.datetime(2014, 1, 1, 0, 0), datetime.datetime(2014, 1, 2, 0, 0)): 'foo'}
Weeeelllll... at least I learned about slicing and that kind of stuff... LoL...