Re: sub-classing the types in the builtin module datetime

Discussion in 'Python' started by Colin J. Williams, Aug 16, 2007.

  1. I posted this about 5 hours ago, but it seems to have gone astray.

    cjw


    Jay Loden wrote:
    > Colin J. Williams wrote:
    >
    >> I wish to sub-class (if that's the right word) datetime and to use a
    >> different signature for the constructor.
    >>
    >> The second part has gone smoothly, but it is difficult to access the
    >> type's methods from the sub-class instance.
    >>
    >> I'm beginning to wonder whether it might might be simpler to write my
    >> own Date class.
    >>
    >> Does anyone have any comments please?
    >>
    >> Colin W.
    >>

    >
    > I think it would be most effective if you can post the code you're

    using that illustrates the issue, and what you're trying to do so that
    the list members can analyze the actual problem. Tracebacks etc are
    also welcome if applicable.
    >
    > -Jay
    >
    >

    Jay,

    Yes, I should have posted an example, but I thought that others might
    have experienced the problem.

    It is illustrated at the bottom of this script:

    # subClassing.py

    import datetime
    import new
    import sys
    import types

    class Date(datetime.datetime):
    ''' Date(s) -> a date object.__class__
    where s is an 8 digit string'''

    def __new__(cls, YYmmdd):
    ''' YYmmdd is a string, in the form yyyymmdd i.e. 8 digits.
    or a 3-tuple of integers in the form (y, m, d)
    or a 6-tuple of integers in the form (y, m, d, h, m,
    s) '''
    #todo needs a cleanup
    print '__new__'
    self= datetime.datetime.__new__(cls, 1, 1, 1)
    ln= len(YYmmdd)
    if type(YYmmdd) not in (types.StringType, types.TupleType):
    raise ValueError, str(YYmmdd) + ' is invalid for Date'
    ids= ('year', 'momth', 'day', 'hour', 'minute', 'second',
    'microsecond')
    if type(YYmmdd) is types.StringType:
    if ln is 8:
    try:
    values= [int((YYmmdd[:4], YYmmdd[4:6], YYmmdd[6:8])) for
    i in range(3)]
    except:
    raise ValueError, YYmmdd + ' should be an eight digit date
    string'
    else:
    raise ValueError, YYmmdd + ' should be an eight digit date
    string'
    elif ln in (3, 6):
    try:
    for i in range(ln):
    if type(YYmmdd) == types.IntType:
    self.__setattr__(ids, YYmmdd)
    else:
    raise ValueError, str(YYmmdd) + ' contains a non-integer'
    except:
    print sys.exc_info()
    raise ValueError, 'This should not occur, drop after tests #todo'
    else:
    raise ValueError, str(YYmmdd) + ' is not of an acceptable length'

    return datetime.datetime.__new__(cls, *values)

    def __repr__(self):
    return self.__class__.__name__ + ' : ' + str(self)

    def __str__(self):
    return "%04d%02d%02d" % (self.year, self.month, self.day)

    ## # Failures of subclassing
    ## def ctime(self):
    ## ''' ctime: Not available in Date. '''
    ## pass
    ## def date(self):
    ## pass
    ## def now(self):
    ## pass
    ## def today(self):
    ## pass

    a= datetime.datetime(2007, 7, 31)
    d= Date('20070731')
    tm= datetime.time(1, 2)
    try:
    print a.today()
    # print d.today() # grief
    print a.now()
    # print d.now() # grief
    print a.combine(a, tm) # OK, but why not a.combine(tm)?
    # e= d.combine(d, tm) # grief
    print a.utcnow()
    # print d.utcnow() # grief
    print a.ctime()
    print d.ctime()
    except:
    print 'Grief'
    print sys.exc_info()

    Colin W.
     
    Colin J. Williams, Aug 16, 2007
    #1
    1. Advertising

  2. Colin J. Williams

    Guest

    On Aug 15, 5:54 pm, "Colin J. Williams" <> wrote:
    > I posted this about 5 hours ago, but it seems to have gone astray.



    (snipped)

    > >
    > >> I wish to sub-class (if that's the right word) datetime and to use a
    > >> different signature for the constructor.
    > >>
    > >> The second part has gone smoothly, but it is difficult to access the
    > >> type's methods from the sub-class instance.
    > >>
    > >> I'm beginning to wonder whether it might might be simpler to write my
    > >> own Date class.
    > >>
    > >> Does anyone have any comments please?
    > >>
    > >> Colin W.


    (snipped)


    > Yes, I should have posted an example, but I thought that others might
    > have experienced the problem.
    >
    > It is illustrated at the bottom of this script:
    >
    > # subClassing.py
    >
    > import datetime
    > import new
    > import sys
    > import types
    >
    > class Date(datetime.datetime):
    > ''' Date(s) -> a date object.__class__
    > where s is an 8 digit string'''
    >
    > def __new__(cls, YYmmdd):
    > ''' YYmmdd is a string, in the form yyyymmdd i.e. 8 digits.
    > or a 3-tuple of integers in the form (y, m, d)
    > or a 6-tuple of integers in the form (y, m, d, h, m,
    > s) '''



    (snipped)

    >
    > a= datetime.datetime(2007, 7, 31)
    > d= Date('20070731')
    > tm= datetime.time(1, 2)
    > try:
    > print a.today()
    > # print d.today() # grief
    > print a.now()
    > # print d.now() # grief
    > print a.combine(a, tm) # OK, but why not a.combine(tm)?
    > # e= d.combine(d, tm) # grief
    > print a.utcnow()
    > # print d.utcnow() # grief
    > print a.ctime()
    > print d.ctime()
    > except:
    > print 'Grief'
    > print sys.exc_info()
    >
    > Colin W.






    This problem arises when you change the function signature of __new__.
    I'm a little unclear as to why but it seems for the classmethods
    (thosed marked with the METH_CLASS flag in the C source code), you
    need to arrange to bypass the normal method resolution (I used a
    metaclass
    to do this):



    import datetime

    class Date(datetime.datetime):
    pass

    class FixClassMethods(type):
    def __init__(cls, classname, bases, classdict):
    # add strptime if using Python 2.5
    flagged_as_meth_class = ('today', 'now', 'fromtimestamp',
    'fromordinal', 'now', 'utcnow', 'utcfromtimestamp', 'combine')
    for meth in flagged_as_meth_class:
    setattr(cls, meth, getattr(datetime.datetime, meth))

    class DateChangesNewSignature(datetime.datetime):
    @staticmethod
    def str2ymd(strval):
    yyyy, mm, dd = (int(substr) for substr in (strval[:4],
    strval[4:6], strval[6:]))
    return yyyy, mm, dd

    def __new__(cls, strval):
    yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    return super(DateChangesNewSignature,cls).__new__(cls, yyyy,
    mm,
    dd)
    def __init__(self, strval):
    yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    super(DateChangesNewSignature, self).__init__(yyyy, mm,
    dd)

    class DLast(DateChangesNewSignature):
    __metaclass__ = FixClassMethods

    f = Date(2007,07,07)
    print f
    print f.today()

    f2 = DateChangesNewSignature("20070707")
    print f2
    try:
    print f2.today()
    except TypeError, e:
    print str(e)
    print "Uh?"


    f3 = DLast("20070707")
    print f3
    print f3.today()


    I get:

    2007-07-07 00:00:00
    2007-08-16 12:57:41.480679
    2007-07-07 00:00:00
    __new__() takes exactly 2 arguments (9 given)
    Uh?
    2007-07-07 00:00:00
    2007-08-16 12:57:41.483104


    --
    Hope this helps,
    Steven
     
    , Aug 16, 2007
    #2
    1. Advertising

  3. wrote:
    > On Aug 15, 5:54 pm, "Colin J. Williams" <> wrote:
    >> I posted this about 5 hours ago, but it seems to have gone astray.

    >
    >
    > (snipped)
    >
    >> >
    >> >> I wish to sub-class (if that's the right word) datetime and to use a
    >> >> different signature for the constructor.
    >> >>
    >> >> The second part has gone smoothly, but it is difficult to access the
    >> >> type's methods from the sub-class instance.
    >> >>
    >> >> I'm beginning to wonder whether it might might be simpler to write my
    >> >> own Date class.
    >> >>
    >> >> Does anyone have any comments please?
    >> >>
    >> >> Colin W.

    >
    > (snipped)
    >
    >
    >> Yes, I should have posted an example, but I thought that others might
    >> have experienced the problem.
    >>
    >> It is illustrated at the bottom of this script:
    >>
    >> # subClassing.py
    >>
    >> import datetime
    >> import new
    >> import sys
    >> import types
    >>
    >> class Date(datetime.datetime):
    >> ''' Date(s) -> a date object.__class__
    >> where s is an 8 digit string'''
    >>
    >> def __new__(cls, YYmmdd):
    >> ''' YYmmdd is a string, in the form yyyymmdd i.e. 8 digits.
    >> or a 3-tuple of integers in the form (y, m, d)
    >> or a 6-tuple of integers in the form (y, m, d, h, m,
    >> s) '''

    >
    >
    > (snipped)
    >
    >> a= datetime.datetime(2007, 7, 31)
    >> d= Date('20070731')
    >> tm= datetime.time(1, 2)
    >> try:
    >> print a.today()
    >> # print d.today() # grief
    >> print a.now()
    >> # print d.now() # grief
    >> print a.combine(a, tm) # OK, but why not a.combine(tm)?
    >> # e= d.combine(d, tm) # grief
    >> print a.utcnow()
    >> # print d.utcnow() # grief
    >> print a.ctime()
    >> print d.ctime()
    >> except:
    >> print 'Grief'
    >> print sys.exc_info()
    >>
    >> Colin W.

    >
    >
    >
    >
    >
    > This problem arises when you change the function signature of __new__.
    > I'm a little unclear as to why but it seems for the classmethods
    > (thosed marked with the METH_CLASS flag in the C source code), you
    > need to arrange to bypass the normal method resolution (I used a
    > metaclass
    > to do this):
    >
    >
    >
    > import datetime
    >
    > class Date(datetime.datetime):
    > pass
    >
    > class FixClassMethods(type):
    > def __init__(cls, classname, bases, classdict):
    > # add strptime if using Python 2.5
    > flagged_as_meth_class = ('today', 'now', 'fromtimestamp',
    > 'fromordinal', 'now', 'utcnow', 'utcfromtimestamp', 'combine')
    > for meth in flagged_as_meth_class:
    > setattr(cls, meth, getattr(datetime.datetime, meth))
    >
    > class DateChangesNewSignature(datetime.datetime):
    > @staticmethod
    > def str2ymd(strval):
    > yyyy, mm, dd = (int(substr) for substr in (strval[:4],
    > strval[4:6], strval[6:]))
    > return yyyy, mm, dd
    >
    > def __new__(cls, strval):
    > yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    > return super(DateChangesNewSignature,cls).__new__(cls, yyyy,
    > mm,
    > dd)
    > def __init__(self, strval):
    > yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    > super(DateChangesNewSignature, self).__init__(yyyy, mm,
    > dd)
    >
    > class DLast(DateChangesNewSignature):
    > __metaclass__ = FixClassMethods
    >
    > f = Date(2007,07,07)
    > print f
    > print f.today()
    >
    > f2 = DateChangesNewSignature("20070707")
    > print f2
    > try:
    > print f2.today()
    > except TypeError, e:
    > print str(e)
    > print "Uh?"
    >
    >
    > f3 = DLast("20070707")
    > print f3
    > print f3.today()
    >
    >
    > I get:
    >
    > 2007-07-07 00:00:00
    > 2007-08-16 12:57:41.480679
    > 2007-07-07 00:00:00
    > __new__() takes exactly 2 arguments (9 given)
    > Uh?
    > 2007-07-07 00:00:00
    > 2007-08-16 12:57:41.483104
    >
    >
    > --
    > Hope this helps,
    > Steven
    >

    Steven,

    Many thanks, I'll try this out tomorrow.

    Colin W
     
    Colin J. Williams, Aug 16, 2007
    #3
  4. wrote:
    > On Aug 15, 5:54 pm, "Colin J. Williams" <> wrote:
    >> I posted this about 5 hours ago, but it seems to have gone astray.

    >
    >
    > (snipped)
    >
    >> >
    >> >> I wish to sub-class (if that's the right word) datetime and to use a
    >> >> different signature for the constructor.
    >> >>
    >> >> The second part has gone smoothly, but it is difficult to access the
    >> >> type's methods from the sub-class instance.
    >> >>
    >> >> I'm beginning to wonder whether it might might be simpler to write my
    >> >> own Date class.
    >> >>
    >> >> Does anyone have any comments please?
    >> >>
    >> >> Colin W.

    >
    > (snipped)
    >
    >
    >> Yes, I should have posted an example, but I thought that others might
    >> have experienced the problem.
    >>
    >> It is illustrated at the bottom of this script:
    >>
    >> # subClassing.py
    >>
    >> import datetime
    >> import new
    >> import sys
    >> import types
    >>
    >> class Date(datetime.datetime):
    >> ''' Date(s) -> a date object.__class__
    >> where s is an 8 digit string'''
    >>
    >> def __new__(cls, YYmmdd):
    >> ''' YYmmdd is a string, in the form yyyymmdd i.e. 8 digits.
    >> or a 3-tuple of integers in the form (y, m, d)
    >> or a 6-tuple of integers in the form (y, m, d, h, m,
    >> s) '''

    >
    >
    > (snipped)
    >
    >> a= datetime.datetime(2007, 7, 31)
    >> d= Date('20070731')
    >> tm= datetime.time(1, 2)
    >> try:
    >> print a.today()
    >> # print d.today() # grief
    >> print a.now()
    >> # print d.now() # grief
    >> print a.combine(a, tm) # OK, but why not a.combine(tm)?
    >> # e= d.combine(d, tm) # grief
    >> print a.utcnow()
    >> # print d.utcnow() # grief
    >> print a.ctime()
    >> print d.ctime()
    >> except:
    >> print 'Grief'
    >> print sys.exc_info()
    >>
    >> Colin W.

    >
    >
    >
    >
    >
    > This problem arises when you change the function signature of __new__.
    > I'm a little unclear as to why but it seems for the classmethods
    > (thosed marked with the METH_CLASS flag in the C source code), you
    > need to arrange to bypass the normal method resolution (I used a
    > metaclass
    > to do this):
    >
    >
    >
    > import datetime
    >
    > class Date(datetime.datetime):
    > pass
    >
    > class FixClassMethods(type):
    > def __init__(cls, classname, bases, classdict):
    > # add strptime if using Python 2.5
    > flagged_as_meth_class = ('today', 'now', 'fromtimestamp',
    > 'fromordinal', 'now', 'utcnow', 'utcfromtimestamp', 'combine')
    > for meth in flagged_as_meth_class:
    > setattr(cls, meth, getattr(datetime.datetime, meth))
    >
    > class DateChangesNewSignature(datetime.datetime):
    > @staticmethod
    > def str2ymd(strval):
    > yyyy, mm, dd = (int(substr) for substr in (strval[:4],
    > strval[4:6], strval[6:]))
    > return yyyy, mm, dd
    >
    > def __new__(cls, strval):
    > yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    > return super(DateChangesNewSignature,cls).__new__(cls, yyyy,
    > mm,
    > dd)
    > def __init__(self, strval):
    > yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    > super(DateChangesNewSignature, self).__init__(yyyy, mm,
    > dd)
    >
    > class DLast(DateChangesNewSignature):
    > __metaclass__ = FixClassMethods
    >
    > f = Date(2007,07,07)
    > print f
    > print f.today()
    >
    > f2 = DateChangesNewSignature("20070707")
    > print f2
    > try:
    > print f2.today()
    > except TypeError, e:
    > print str(e)
    > print "Uh?"
    >
    >
    > f3 = DLast("20070707")
    > print f3
    > print f3.today()
    >
    >
    > I get:
    >
    > 2007-07-07 00:00:00
    > 2007-08-16 12:57:41.480679
    > 2007-07-07 00:00:00
    > __new__() takes exactly 2 arguments (9 given)
    > Uh?
    > 2007-07-07 00:00:00
    > 2007-08-16 12:57:41.483104
    >
    >
    > --
    > Hope this helps,
    > Steven
    >

    Steven,

    Many thanks, I'll try this out tomorrow.

    Colin W
     
    Colin J. Williams, Aug 16, 2007
    #4
  5. wrote:
    [My question snipped]
    >
    >
    >
    > This problem arises when you change the function signature of __new__.
    > I'm a little unclear as to why but it seems for the classmethods
    > (thosed marked with the METH_CLASS flag in the C source code), you
    > need to arrange to bypass the normal method resolution (I used a
    > metaclass
    > to do this):
    >
    >
    >
    > import datetime
    >
    > class Date(datetime.datetime):
    > pass
    >
    > class FixClassMethods(type):
    > def __init__(cls, classname, bases, classdict):
    > # add strptime if using Python 2.5
    > flagged_as_meth_class = ('today', 'now', 'fromtimestamp',
    > 'fromordinal', 'now', 'utcnow', 'utcfromtimestamp', 'combine')
    > for meth in flagged_as_meth_class:
    > setattr(cls, meth, getattr(datetime.datetime, meth))
    >
    > class DateChangesNewSignature(datetime.datetime):
    > @staticmethod
    > def str2ymd(strval):
    > yyyy, mm, dd = (int(substr) for substr in (strval[:4],
    > strval[4:6], strval[6:]))
    > return yyyy, mm, dd
    >
    > def __new__(cls, strval):
    > yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    > return super(DateChangesNewSignature,cls).__new__(cls, yyyy,
    > mm,
    > dd)
    > def __init__(self, strval):
    > yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    > super(DateChangesNewSignature, self).__init__(yyyy, mm,
    > dd)
    >
    > class DLast(DateChangesNewSignature):
    > __metaclass__ = FixClassMethods
    >
    > f = Date(2007,07,07)
    > print f
    > print f.today()
    >
    > f2 = DateChangesNewSignature("20070707")
    > print f2
    > try:
    > print f2.today()
    > except TypeError, e:
    > print str(e)
    > print "Uh?"
    >
    >
    > f3 = DLast("20070707")
    > print f3
    > print f3.today()
    >
    >
    > I get:
    >
    > 2007-07-07 00:00:00
    > 2007-08-16 12:57:41.480679
    > 2007-07-07 00:00:00
    > __new__() takes exactly 2 arguments (9 given)
    > Uh?
    > 2007-07-07 00:00:00
    > 2007-08-16 12:57:41.483104
    >
    >
    > --
    > Hope this helps,
    > Steven
    >


    Steven,

    Thanks, you provide an elegant solution to the datetime problem I raised.

    I like the illustration of metaclass usage you have have given,
    it's something I have had trouble grasping.

    You handle the examples I gave. However, on reflection,I feel that
    ('today', 'now', 'fromtimestamp', 'fromordinal', 'now', 'utcnow',
    'utcfromtimestamp', 'combine') are completely inappropriate as
    methods and that they should have been set up as functions of
    datetime and not as methods of datetime.datetime.
    The difficulty I have in adopting your approach is that it would
    be difficult for the reader to comprehend the code.

    My feeling is that it should be possible to change a signature using
    simple Python approaches.

    I'll puzzle some more.

    Thanks again.

    Colin W.
     
    Colin J. Williams, Aug 17, 2007
    #5
  6. wrote:
    [My question snipped]
    >
    >
    >
    > This problem arises when you change the function signature of __new__.
    > I'm a little unclear as to why but it seems for the classmethods
    > (thosed marked with the METH_CLASS flag in the C source code), you
    > need to arrange to bypass the normal method resolution (I used a
    > metaclass
    > to do this):
    >
    >
    >
    > import datetime
    >
    > class Date(datetime.datetime):
    > pass
    >
    > class FixClassMethods(type):
    > def __init__(cls, classname, bases, classdict):
    > # add strptime if using Python 2.5
    > flagged_as_meth_class = ('today', 'now', 'fromtimestamp',
    > 'fromordinal', 'now', 'utcnow', 'utcfromtimestamp', 'combine')
    > for meth in flagged_as_meth_class:
    > setattr(cls, meth, getattr(datetime.datetime, meth))
    >
    > class DateChangesNewSignature(datetime.datetime):
    > @staticmethod
    > def str2ymd(strval):
    > yyyy, mm, dd = (int(substr) for substr in (strval[:4],
    > strval[4:6], strval[6:]))
    > return yyyy, mm, dd
    >
    > def __new__(cls, strval):
    > yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    > return super(DateChangesNewSignature,cls).__new__(cls, yyyy,
    > mm,
    > dd)
    > def __init__(self, strval):
    > yyyy, mm, dd = DateChangesNewSignature.str2ymd(strval)
    > super(DateChangesNewSignature, self).__init__(yyyy, mm,
    > dd)
    >
    > class DLast(DateChangesNewSignature):
    > __metaclass__ = FixClassMethods
    >
    > f = Date(2007,07,07)
    > print f
    > print f.today()
    >
    > f2 = DateChangesNewSignature("20070707")
    > print f2
    > try:
    > print f2.today()
    > except TypeError, e:
    > print str(e)
    > print "Uh?"
    >
    >
    > f3 = DLast("20070707")
    > print f3
    > print f3.today()
    >
    >
    > I get:
    >
    > 2007-07-07 00:00:00
    > 2007-08-16 12:57:41.480679
    > 2007-07-07 00:00:00
    > __new__() takes exactly 2 arguments (9 given)
    > Uh?
    > 2007-07-07 00:00:00
    > 2007-08-16 12:57:41.483104
    >
    >
    > --
    > Hope this helps,
    > Steven
    >


    Steven,

    Thanks, you provide an elegant solution to the datetime problem I raised.

    I like the illustration of metaclass usage you have have given,
    it's something I have had trouble grasping.

    You handle the examples I gave. However, on reflection,I feel that
    ('today', 'now', 'fromtimestamp', 'fromordinal', 'now', 'utcnow',
    'utcfromtimestamp', 'combine') are completely inappropriate as
    methods and that they should have been set up as functions of
    datetime and not as methods of datetime.datetime.
    The difficulty I have in adopting your approach is that it would
    be difficult for the reader to comprehend the code.

    My feeling is that it should be possible to change a signature using
    simple Python approaches.

    I'll puzzle some more.

    Thanks again.

    Colin W.
     
    Colin J. Williams, Aug 17, 2007
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Pritam Kamat

    Obfuscation and sub classing

    Pritam Kamat, Aug 8, 2003, in forum: Java
    Replies:
    2
    Views:
    527
    pete kirkham
    Aug 8, 2003
  2. Colin J. Williams

    Sub-classing NumArray - two questions

    Colin J. Williams, Oct 1, 2003, in forum: Python
    Replies:
    0
    Views:
    274
    Colin J. Williams
    Oct 1, 2003
  3. Bernard Lebel

    Problem with sub-classing

    Bernard Lebel, Jul 17, 2006, in forum: Python
    Replies:
    3
    Views:
    372
    Peter Otten
    Jul 17, 2006
  4. Colin J. Williams
    Replies:
    1
    Views:
    272
    Michael Amrhein
    Aug 15, 2007
  5. Colin J. Williams

    sub-classing datetime

    Colin J. Williams, Feb 7, 2013, in forum: Python
    Replies:
    1
    Views:
    117
    marduk
    Feb 7, 2013
Loading...

Share This Page