I can think of many reasons why datetime is different from
builtins, though to be honest I'm not sure that consistency for
its own sake is really a strong argument for keeping a
counter-intuitive behavior - and to be honest I'm open to the idea
that all arithmetic types should have some form of
this change.
That said, I would say that the biggest difference between
datetime and builtins (other than the fact that datetime is not
a builtin, and as such doesn't necessarily need to be categorized
in this group), is that unlike almost all other arithmetic types,
datetime has a special, dedicated type for describing
differences in datetimes. Using your example of a float subclass,
consider that without the behavior of "addition of floats returns
floats", it would be hard to predict what would happen in this
situation:
>>> F(1.2) + 3.4
Would that always return a float, even though F(1.2) + F(3.4)
returns an F? Would that return an F because F is the left-hand
operand? Would it return a float because float is the right-hand
operand? Would you walk the MROs and find the lowest type in
common between the operands and return that? It's not entirely
clear which subtype predominates. With datetime, you have:
datetime - datetime -> timedelta
datetime ± timedelta -> datetime
timedelta ± timedelta -> timedelta
There's no operation between two datetime objects that would
return a datetime object, so it's always clear: operations between
datetime subclasses return timedelta, operations between a
datetime object and a timedelta return the subclass of the
datetime that it was added to or subtracted from.
Of course, the real way to resolve whether datetime should be
different from int/float/string/etc is to look at why this choice
was actually made for those types in the first place, and decide
whether datetime is like them in this respect. The
heterogeneous operations problem may be a reasonable justification
for leaving the other builtins alone but changing datetime, but if
someone knows of other fundamental reasons why the decision to
have arithmetic operations always create the base class was
chosen, please let me know.
Best,
Paul
On Wed, Jan 2, 2019 at 10:18 PM Paul Ganssle <paul@ganssle.io> wrote:
.. the original objection was that this implementation assumes that the datetime subclass has a constructor with the same (or a sufficiently similar) signature as datetime.
While this was used as a possible rationale for the way standard types behave, the main objection to changing datetime classes is that it will make them behave differently from builtins. For example:
>>> class F(float):... pass...>>> type(F.fromhex('AA'))<class '__main__.F'>>>> type(F(1) + F(2))<class 'float'>
This may be a legitimate gripe, but unfortunately that ship has sailed long ago. All of datetime's alternate constructors make this assumption. Any subclass that does not meet this requirement must have worked around it long ago (or they don't care about alternate constructors).
This is right, but the same argument is equally applicable to int, float, etc. subclasses. If you want to limit your change to datetime types you should explain what makes these types special.