4

How can I subclass from numpy datetime64 ? For instance using the standard datetime I can easily subclass:

import datetime as dt

class SubFromDateTime(dt.datetime):
    def __new__(cls):
        return dt.datetime.__new__(cls, 2012, 1, 1)

print type(SubFromDateTime())

>>> 
<class '__main__.SubFromDateTime'>

However, using datetime64 the following always returns the datetime64 reference, not my class...

from numpy import datetime64

class SubFromDT64(datetime64):
    def __new__(cls):
        return datetime64.__new__(cls, '20120101')

print type(SubFromDT64())

>>>
<type 'numpy.datetime64'>

How can I fix this? I'd basically like to write a simple wrapper for datetime64, which allows me to add custom functions, such as obtaining the month of a given date with a simple .Month() method. With the upper example I can easily add methods, in the lower example it will never recognize my methods and thinks it's a datetime64 object.

L0tad
  • 574
  • 3
  • 15
Muppet
  • 5,767
  • 6
  • 29
  • 39
  • 1
    As a side note, it's a bit misleading to call `__new__`'s first argument `self` instead of `cls`. Also, why aren't you using `super`? – abarnert Nov 24 '14 at 22:41
  • Anyway, for the general question, types are supposed to usually return an instance of `cls` (or a subclass thereof) when you call `__new__(cls)`, but there's no rule that they must, and nothing you can do about it if they don't. For the numpy-specific question… there probably is an answer, but I don't know it off the top of my head, so hopefully someone else does. :) – abarnert Nov 24 '14 at 22:44
  • One quick thing I tried is to change the `__class__` attribute, but NumPy declares its base types as non-heap types, so that won't work… – abarnert Nov 24 '14 at 22:45
  • 1
    One last thing: Why are you trying to do this? If you store these things in an `ndarray`, your `dtype` is either going to be `datetime64`, `object`, or a custom `dtype` that stores the values as `datetime64` and treats them as such in C API functions but auto-converts them when you box them up for Python. Unless you want `object`, it really doesn't matter whether the type inherits `datetime64`. – abarnert Nov 24 '14 at 22:52
  • 1
    Thanks for trying. I want to subclass because I want to keep the original behaviour of datetime64, but add some custom functions. Just like one can subclass to ndarray (and there are many examples for that) I'm looking to subclass the very similar datetime64 class. – Muppet Nov 24 '14 at 23:09
  • The question is: do you want to put these things in arrays? If not, there are probably better options than `np.datetime64`. If so, subclassing won't help, because that's not how NumPy arrays work. They don't store Python objects, they store "native type" objects, and convert to and from Python objects when you access them from Python. – abarnert Nov 24 '14 at 23:19

1 Answers1

4

I ended up subclassing ndarray which creates a datetime64 array. Works like a charm for my purposes. In case anyone is interested here the code:

import numpy as np

class Date64(np.ndarray):
    def __new__(cls, data):
        data = np.asarray(data, dtype='datetime64')
        if (data.dtype != 'datetime64[D]'):
            raise Exception('Unable to parse dates adequately to datetime64[D]: %s' % data)
        obj = data.view(cls)
        return obj

    def Year(self):
        return self.astype('datetime64[Y]').astype(int) + 1970

    def Month(self):
        return self.astype('datetime64[M]').astype(int) % 12 + 1

    def Day(self):
        return (self - self.astype('datetime64[M]') + 1).astype(int)

    def ISO(self):
        if (self.shape):
            out = zip(self.Year(), self.Month(), self.Day())
            iso = [ '%04d-%02d-%02d' % each for each in out ]
        else:
            iso = '%04d-%02d-%02d' % (self.Year(), self.Month(), self.Day())
        return iso

    def Export(self):
        return self

    def __array_finalize__(self, obj):
        if obj is None:
            return


if (__name__ == '__main__'):
    a = [ dt.date(2013, 1, 1), '2012-03-01', '2012-07-02', '2012-01-03', '2012-01-04', '2011-01-05' ]
    b = Date64(a)

    print b
    print b.ISO()
farenorth
  • 10,165
  • 2
  • 39
  • 45
Muppet
  • 5,767
  • 6
  • 29
  • 39