I have a couple classes extending builtin datetime.*
Is there any good reason to not overload + (MyTime.__radd___
) so MyDate + MyTime returns a MyDateTime?
This is already implemented as a class method, datetime.datetime.combine
:
import datetime
d = datetime.date(2010, 12, 5)
t = datetime.time(10, 22, 15)
dt = datetime.datetime.combine(d, t)
print dt
prints
2010-12-05 10:22:15
This would generally be frowned upon because you're really combining rather than adding; this is why the actual datetime library has a combine method rather than using addition in this way.
I'm not aware of any other cases in Python where <instance of TypeA> + <instance of TypeB>
produces <instance of TypeC>
. Thus, the Principle of least astonishment suggests that you should simply provide a combine
method rather than overload addition.
Yes, there is at least one good reason not to: the resulting instance is completely different from the two input instances. Is this important? I don't think so -- consider that date - date
yields timedelta
.
The way I see it:
and for subtraction
Developing along the lines of what makes sense:
date + time => datetime
date + timedelta => date | datetime or exception or silently drop time portion
time + date => datetime
time + timedelta => time | wrap-around or exception
date - date => timedelta
date - timedelta => date | datetime or exception or silently drop time portion
time - time => timedelta
time - timedelta => time | wrap-around or exception
datetime + timedelta => datetime
datetime - timedelta => datetime
So, if it were me and I were designing a Date, Time, DateTime, TimeDelta framework, I would allow:
date + time
date - date
time - time
datetime + timedelta
datetime - timedelta
and for these:
date +/- timedelta
time +/- timedelta
I would default to returning the same type if the timedelta had none of the other type, and raising an exception if the timedelta did have some of the other type, but there would be a setting that would control that. The other possible behavior would be to drop the unneeded portion -- so a date combined with a timedelta that had hours would drop the hours and return a date.
Due to the existence of the date, time, and datetime cross-type addition and subtraction operators, I would think that this is fine, so long as it is well defined.
Currently (2.7.2):
date = date + timedelta
date = date - timedelta
timedelta = date - date
datetime = datetime + timedelta
datetime = datetime - timedelta
timedelta = datetime - datetime
I believe the following is also reasonable for an extension:
timedelta = time - time
datetime = date + time
I was going to suggest the following as well, but time
has very specific min and max values for hour
, minute
, second
, and microsecond
, thus requiring a silent wraparound of values or returning of a different type:
time = time + timedelta
time = time - timedelta
Similarly, date
cannot handle a timedelta
of less than a day being added to it. Often I have been told to simply use Duck Typing with Python, because that's the intent. If that is true, then I would propose the following completed interface:
[date|datetime] = date + timedelta
[date|datetime] = date - timedelta
timedelta = date - date
[time|timedelta] = time + timedelta
[time|timedelta] = time - timedelta
timedelta = time - time
datetime = datetime + timedelta
datetime = datetime - timedelta
datetime = date + time
datetime = date - time
timedelta = datetime - datetime
timedelta = datetime - date
timedelta = timedelta + timedelta
timedelta = timedelta - timedelta
In which, given the case that date
has precision loss (for timedelta
's with partial days), it is promoted to datetime
. Similarly, given the case that time
has precision loss (for timedelta
's that yield a result of more than one day, or negative time), it is promoted to timedelta
. However, I'm not fully comfortable with [time|timedelta]
. It makes sense given the rest of the interface from parallelism and precision views, but I do think it might be more elegant to just wraparound the time to the proper hour, thus changing all the [time|timedelta]
's to simply time
, but unfortunately that leaves us with lost precision.
In my opinion, the most valuable uses of operator overloading are situations where many input values can be combined. You'd never want to deal with:
concat(concat(concat("Hello", ", "), concat("World", "!")), '\n');
or
distance = sqrt(add(add(x*x, y*y), z*z));
So we overload math symbols to create a more intuitive syntax. Another way to deal with this problem is variadic functions, like +
in Scheme.
With your date + time = datetime
, it doesn't make sense to add datetime + datetime
, datetime + time
, or datetime + date
, so you could never encounter a situation like those above.
In my opinion, once again, the right thing is to use a constructor method. In a language with strong typing like C++, you'd have DateTime(const Date &d, const Time &t)
. With Python's dynamic typing, I guess they gave the function a name, datetime.combine(date, time)
, to make the code clearer when the types of the input variables are not visible in the code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With