Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Re: sub-classing the types in the builtin module datetime

Reply
Thread Tools

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

 
 
Colin J. Williams
Guest
Posts: n/a
 
      08-16-2007
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])[i]) 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[i]) == types.IntType:
self.__setattr__(ids[i], YYmmdd[i])
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.

 
Reply With Quote
 
 
 
 
attn.steven.kuo@gmail.com
Guest
Posts: n/a
 
      08-16-2007
On Aug 15, 5:54 pm, "Colin J. Williams" <(E-Mail Removed)> 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

 
Reply With Quote
 
 
 
 
Colin J. Williams
Guest
Posts: n/a
 
      08-16-2007
http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:
> On Aug 15, 5:54 pm, "Colin J. Williams" <(E-Mail Removed)> 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

 
Reply With Quote
 
Colin J. Williams
Guest
Posts: n/a
 
      08-16-2007
(E-Mail Removed) wrote:
> On Aug 15, 5:54 pm, "Colin J. Williams" <(E-Mail Removed)> 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
 
Reply With Quote
 
Colin J. Williams
Guest
Posts: n/a
 
      08-17-2007
(E-Mail Removed) 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.
 
Reply With Quote
 
Colin J. Williams
Guest
Posts: n/a
 
      08-17-2007
(E-Mail Removed) 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.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
subtle error slows code by 10x (builtin sum()) - replace builtin sumwithout using import? bdb112 Python 2 07-02-2011 03:13 AM
sub-classing the types in the builtin module datetime Colin J. Williams Python 1 08-15-2007 05:03 PM
mx.DateTime to datetime.datetime mp Python 1 07-28-2006 10:57 PM
datetime: .datetime-.datetime = .timedelta, .time-.time=TypeError ? Christos TZOTZIOY Georgiou Python 3 09-13-2003 10:44 AM
RE: datetime: .datetime-.datetime = .timedelta, .time-.time=TypeError ? Tim Peters Python 0 09-09-2003 12:57 AM



Advertisments