Python using datetime snippet as keyword

Can I be used datetime

slice

as a dictionary key?

Example:

st = datetime.datetime(2010, 12, 31, 0, 0)
en = datetime.datetime(2011, 12, 28, 0, 0)
b = [] 
b[slice(st,en)] = 'some_random_value_could_be_anything' # Error!

      

One of two errors occurs:

In the case of a single dictionary:

TypeError: slice indices must be integers or None or have an __index__ method

      

In the case of a nested dictionary:

TypeError: unhashable type

      

+3


source to share


2 answers


Disclaimer . This answer mainly explains to show one (of the many) things that Python allows you to do. Also, you should probably NOT want to do things this way (just because you can do things doesn't mean you should)

It said the documentation for getitem (self, key) says:

For sequence types, the received keys must be integers and object slices.

which means that any class that wants to mimic the behavior of a type type (like a list) must be prepared for implementation __getitem__

where the key can be of type
slice . This (sort of) explains (or at least one way or another) why you get in your dictionary TypeError: unhashable type

when you try to do b[slice(st,en)] = 'bla '

: it's because it's trying to use the instance slice(st, en)

as a dictionary key. Objects slice

are not hashable and therefore cannot be used as keys dict

. The type is dict

NOT the type of the sequence, so trying to slice the dictionary is irrelevant.

Let's say you have:

  

{... 'foo': 1, ... 'bar': 2, ... 'baz': 3, ...}

  

What does slice

from 'foo'

to mean 'bar'

? Will you return a set of keys in the order you entered them? ('foo', 'bar', 'baz')? Python doesn't know about this. Will this be theirs __hash__

? It's internal, meaningless when it comes to slicing.

All of this said very, very badly here ... but it "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]

      



Output:

{
    datetime.datetime(2014, 1, 2, 0, 0): ('bar',), 
    datetime.datetime(2014, 1, 3, 0, 0): ('baz',)
}

      

But bad, bad, bad ... DatetimeDict

becomes a strange construct that is a dictionary, but at the same time behaves like a type of sequence ... Bad .

EDIT (after re-reading, I'm sure I misunderstood the question)

You didn't really try to slice dict

, where are you? The day I learn to read, I will conquer the world ...: - D

If you want to use a datetimes range as a key dict

, I would recommend that you just put start

and end

in 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 cutting and stuff like that ... LoL ...

+2


source


You can create an object slice

from two dates.

And you can try slicing the list with it. But if the start or end are objects, then list

resolves those objects to an integer value by calling __index__

those objects as specified here . This is the first error you get.

The second error you are getting is that you cannot create an object hash

for an object slice

. Python always generates a value hash

for key objects because dictionaries are like a hash list. Slicing dictionaries is not possible, you can impose a helper function that mimics this behavior in some way. Another apporach will use a tuple as a dictionary key so you can avoid slice

. Tuples are hashables.

You can try your suggested approach with method injection __index__

for extended datetime class.



class MyDateTime(datetime.datetime):
    def __index__(self):
        # in your case return number of minutes (in this case since 1970/1/1)
        return int((self-datetime.datetime(1970,1,1)).total_seconds()/60)

st = MyDateTime(2010, 12, 31, 0, 0)
en = MyDateTime(2011, 12, 28, 0, 0)

b = [] 
b[slice(st,en)] = [100]

      

In this example, you will use the smallest total number corresponding to minutes. This will be most helpful, but larger steps may work for you as well.

But I don't recommend using some time slots with slice

and lists. I bet you will run into other issues like performance or complexity with it.

You haven't added yet what problem you are trying to solve. Maybe I'm right and you want to check DateTimeIntervals for overlap and so on. If so, take a look at DateTimeInterval .

+2


source







All Articles