◐ Shell
clean mode source ↗

Message 107410 - Python tracker

I would like to take another shot at this. The link in Amaury's closing comment no longer works, so here is the relevant post:

"""
Returning same type as self for arithmetic in subclasses

Tim Peters tim.peters at gmail.com 
Sat Jan 8 02:09:27 CET 2005

[Max M]
> """
> I subclass datetime and timedelta
> 
> >>> dt = myDatetime(1970,1,1)
> >>> type(dt)
> <class 'dtime.myDatetime'>
> 
> >>> td = myTimedelta(hours=1)
> >>> type(td)
> <class 'dtime.myTimedelta'>
> 
> But when I do arithmetic with these classes, they return datetime and
> timedelta,
...
> >>> new_time = dt + td
> >>> new_time
> datetime.datetime(1970, 1, 1, 1, 0)
> 
> >>> type(new_time)
> <type 'datetime.datetime'>

Yes, and all builtin Python types work that way.  For example,
int.__add__ or float.__add__ applied to a subclass of int or float
will return an int or float; similarly for a subclass of str.  This
was Guido's decision, based on that an implementation of any method in
a base class has no idea what requirements may exist for invoking a
subclass's constructor.  For example, a subclass may restrict the
values of constructor arguments, or require more arguments than a base
class constructor; it may permute the order of positional arguments in
the base class constructor; it may even be "a feature" that a subclass
constructor gives a different meaning to an argument it shares with
the base class constructor.  Since there isn't a way to guess, Python
does a safe thing instead.

> where I want them to return myDatetime and myTimedelta
>
> So I wondered if there was a simlpler way to coerce the result into my
> desired types rather than overwriting the __add__, __sub__ etc. methods?

Generally speaking, no.  But I'm sure someone will torture you with a
framework that purports to make it easy <wink>.
"""  http://mail.python.org/pipermail/python-list/2005-January/925838.html


As I explained in my previous post, the same argument, "base class has no idea what requirements may exist for invoking a subclass's constructor", applies to class methods, but they nevertheless consistently construct subclass instances:

>>> class d(datetime): pass
>>> d.utcfromtimestamp(0)
d(1970, 1, 1, 0, 0)
>>> d.fromtimestamp(0)
d(1969, 12, 31, 19, 0)
>>> d.combine(date(1,1,1), time(1,1))
d(1, 1, 1, 1, 1)


Similar example for the date class:

>>> class Date(date): pass
>>> Date.fromordinal(1)
Date(1, 1, 1)


In my view it is hard to justify that for a Date instance d, and integer days, Date.fromordinal(d.toordinal() + days) happily produces a Date instance, but d + timedelta(days) returns a basic date instance.