nhavens nhavens - 10 days ago 8
Python Question

Python: how to extend datetime.timedelta

I am trying to extend the Python

datetime.timedelta
for use with cross country race results. I want to construct an object from a string in format
u"mm:ss.s"
. I am able to accomplish this using the factory design pattern and
@classmethod
annotation. How would I accomplish the same by overriding
__init__
and/or
__new__
?

With the code below, constructing an object raises a TypeError. Note that
__init__
is not called, because
'in my __init__'
is not printed.

import datetime
import re

class RaceTimedelta(datetime.timedelta):
def __init__(self, timestr = ''):
print 'in my __init__'
m = re.match(r'(\d+):(\d+\.\d+)', timestr)
if m:
mins = int(m.group(1))
secs = float(m.group(2))
super(RaceTimedelta, self).__init__(minutes = mins, seconds = secs)
else:
raise ValueError('timestr not in format u"mm:ss.d"')


Here is the error:

>>> from mytimedelta import RaceTimedelta
>>> RaceTimedelta(u'24:45.7')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported type for timedelta days component: unicode
>>>


If I move my code from
__init__
to
__new__
, I get the following. Note that this time, the output shows that my
__new__
function is called.

>>> RaceTimedelta(u'24:45.7')
in my __new__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "mytimedelta.py", line 16, in __new__
super(RaceTimedelta, self).__new__(minutes = mins, seconds = secs)
TypeError: datetime.timedelta.__new__(): not enough arguments
>>>

Answer

Apparently timedelta objects are immutable, which means their value is actually set in the class' __new__() method—so you'll need to override that method instead of its __init__():

import datetime
import re

class RaceTimedelta(datetime.timedelta):
    def __new__(cls, timestr=''):
        m = re.match(r'(\d+):(\d+\.\d+)', timestr)
        if m:
            mins, secs = int(m.group(1)), float(m.group(2))
            return super(RaceTimedelta, cls).__new__(cls, minutes=mins, seconds=secs)
        else:
            raise ValueError('timestr argument not in format "mm:ss.d"')

print RaceTimedelta(u'24:45.7')

Output:

0:24:45.700000

BTW, I find it odd that you're providing a default value for thetimestrkeyword argument that will be considered illegal and raise aValueError.

Comments