Issue 18629: future division breaks timedelta division by integer (original) (raw)
Issue18629
Created on 2013-08-02 15:22 by exarkun, last changed 2022-04-11 14:57 by admin. This issue is now closed.
Messages (18) | ||
---|---|---|
msg194181 - (view) | Author: Jean-Paul Calderone (exarkun) * ![]() |
Date: 2013-08-02 15:22 |
datetime.timedelta instances are divisible by integers on Python 2.7, but not when __future__.division has been turned on: exarkun@top:~$ ~/Projects/cpython/2.7/python -c ' from datetime import timedelta print timedelta(seconds=3) / 2 ' 0:00:01.500000 exarkun@top:~$ ~/Projects/cpython/2.7/python -c ' from __future__ import division from datetime import timedelta print timedelta(seconds=3) / 2 ' Traceback (most recent call last): File "", line 4, in TypeError: unsupported operand type(s) for /: 'datetime.timedelta' and 'int' exarkun@top:~$ ~/Projects/cpython/2.7/python Python 2.7.5+ (2.7:8205e72b5cfc, Aug 2 2013, 11:12:04) This presents a minor barrier to Python 3 transitions, since it prevents the use of __future__.division in a module trying to retain Python 2 compatibility where timedelta division is used. | ||
msg194183 - (view) | Author: Antoine Pitrou (pitrou) * ![]() |
Date: 2013-08-02 16:21 |
You're just looking for floor division: $ python -Qnew Python 2.7.4 (default, Apr 19 2013, 18:28:01) [GCC 4.7.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> 1/2 0.5 >>> from datetime import timedelta >>> print timedelta(seconds=3) // 2 0:00:01.500000 | ||
msg194186 - (view) | Author: Jean-Paul Calderone (exarkun) * ![]() |
Date: 2013-08-02 17:05 |
Hm. Maybe I am. Yet isn't true division implemented for this pair of types in Python 3? I'm not sure why it shouldn't be implemented for them in Python 2. Also that raises another question. Does a result of one and one half seconds make sense as the result of a floor division operation? | ||
msg194207 - (view) | Author: Madison May (madison.may) * | Date: 2013-08-02 21:27 |
I agree -- it's not at all intuitive that the floor division returns a decimal number of seconds, while float division raises an error. This should probably be cleaned up. In python 3.4, both float division and integer division return decimal numbers of seconds -- that's not all that intuitive, either. Python 3.4.0a0 (default:d5536c06a082+, Jul 11 2013, 20:23:54) [GCC 4.8.1] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from datetime import timedelta >>> print(timedelta(seconds=3)/2) 0:00:01.500000 >>> print(timedelta(seconds=3)//2) 0:00:01.500000 | ||
msg194216 - (view) | Author: Alexander Belopolsky (belopolsky) * ![]() |
Date: 2013-08-02 22:43 |
I believe this is related to the fact that timedelta * float is not supported in 2.x: Python 2.7.5 (default, May 24 2013, 15:56:16) [GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> from datetime import * >>> timedelta(2) * 0.5 Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for *: 'datetime.timedelta' and 'float' I don't see why this is a barrier to Python 3 transitions. Before you turn on true division you should replace all instances of floor division with '//' anyways. Just treat instances of timedelta / int as you would instances of int / int. | ||
msg194217 - (view) | Author: Alexander Belopolsky (belopolsky) * ![]() |
Date: 2013-08-02 22:47 |
> Does a result of one and one half seconds make sense as the result of a floor division operation? Yes. Timedeltas behave as integers containing the number of microseconds: >>> timedelta(microseconds=1) / 2 datetime.timedelta(0) | ||
msg194246 - (view) | Author: Marc-Andre Lemburg (lemburg) * ![]() |
Date: 2013-08-03 10:48 |
On 03.08.2013 00:47, Alexander Belopolsky wrote: > > Alexander Belopolsky added the comment: > >> Does a result of one and one half seconds make sense as the result of a floor division operation? > > Yes. Timedeltas behave as integers containing the number of microseconds: > >>>> timedelta(microseconds=1) / 2 > datetime.timedelta(0) I think that's a very obscure interpretation of floor division for timedeltas :-) IMO, floor division result should work on seconds, not microseconds. | ||
msg194247 - (view) | Author: Mark Dickinson (mark.dickinson) * ![]() |
Date: 2013-08-03 11:03 |
> I think that's a very obscure interpretation of floor division for > timedeltas :-) Agreed. | ||
msg194271 - (view) | Author: Alexander Belopolsky (belopolsky) * ![]() |
Date: 2013-08-03 16:32 |
What is so special about seconds? Why not days? As in >>> timedelta(3) // 2 timedelta(1) Note that in 3.x we have timedelta over timedelta division that lets you do floor division in arbitrary time units. What is the use case for timedelta // int that rounds down to a second? I suspect in most cases you really want timedelta // timedelta(seconds=int) instead. | ||
msg194272 - (view) | Author: Mark Dickinson (mark.dickinson) * ![]() |
Date: 2013-08-03 16:36 |
I'm not sure I see a use-case for timedelta // int at all. To make sense of that, you first need some way to make sense of floor(timedelta), and as you say it's not clear what that should mean: number of seconds? number of days? Either of those would seem more natural than number of microseconds, though. timedelta // timedelta and timedelta / int on the other hand have an obvious meaning. | ||
msg194273 - (view) | Author: Mark Dickinson (mark.dickinson) * ![]() |
Date: 2013-08-03 16:39 |
-1 on changing the behaviour in 2.7, though; I think it's far too late for that. | ||
msg194274 - (view) | Author: Marc-Andre Lemburg (lemburg) * ![]() |
Date: 2013-08-03 16:43 |
On 03.08.2013 18:32, Alexander Belopolsky wrote: > > Alexander Belopolsky added the comment: > > What is so special about seconds? Why not days? As in > >>>> timedelta(3) // 2 > timedelta(1) > > > Note that in 3.x we have timedelta over timedelta division that lets you do floor division in arbitrary time units. > > What is the use case for timedelta // int that rounds down to a second? I suspect in most cases you really want timedelta // timedelta(seconds=int) instead. The notion of fraction in time usually applies to seconds, not days, hours or minutes. Since floor removes fractions, the natural expectation is to have // int apply to seconds, not microseconds (which represent fractions of a second). That said, I don't think having // division on timedeltas is useful at all. I'd be +1 on removing this support and raise a TypeError instead. | ||
msg194277 - (view) | Author: Tim Peters (tim.peters) * ![]() |
Date: 2013-08-03 17:44 |
Well, a timedelta is a duration. timedelta // n is as close as possible to one n'th of that duration, but "rounding down" (if necessary) so that the result is representable as a timedelta. In the same way, if i and j are integers, i // j is as close as possible to one j'th of i, but "rounding down" (if necessary) so that the result is representable as an integer. Like: >>> from datetime import timedelta >>> timedelta(1) // 7 datetime.timedelta(0, 12342, 857142) >>> timedelta(1) - 7 * _ datetime.timedelta(0, 0, 6) >>> The last line shows the part truncated away: one seventh of a day is not representable as a timedetla. If `timedelta // int` rounded to the closest representable timedelta, it would return timedelta(0, 12342, 857143) instead. That's a little bigger than a seventh of a day: >>> timedelta(0, 12342, 857143) * 7 datetime.timedelta(1, 0, 1) It has nothing directly to do with days, hours, seconds ... it so happens that timedelta has microsecond resolution, so the closest representable approximations to one n'th of a timedelta most often have non-zero microsecond components. If timedelta had femtosecond resolution, they'd most often have non-zero femtosecond components ;-) | ||
msg194278 - (view) | Author: Jean-Paul Calderone (exarkun) * ![]() |
Date: 2013-08-03 17:50 |
> I think that's a very obscure interpretation of floor division for timedeltas :-) Note - I don't care about this. I just want `timedelta / int` to do the same thing in Python 2.7 with __future__.division as `timedelta / int` does in Python 3. Please don't reject this because of some unrelated discussion about how floor division should be implemented for timedelta. | ||
msg194281 - (view) | Author: Alexander Belopolsky (belopolsky) * ![]() |
Date: 2013-08-03 18:19 |
There are two schools of thought here. One school (MAL and Mark) thinks of durations as real number of seconds. The other school (Tim and I) think of durations as integer number of resolution intervals. This is why I and Tim before me resisted adding true division of timedelta by a number and multiplication of timedelta by a float. Mark prevailed on that issue (see #1289118.) His expertise in floating point computing certainly helped designing and implementing these operations correctly. We still have a small quirk waiting to be ironed out (see #8860), but overall I am happy with the current state. This said, the datetime module carries a long legacy of being a pure integer arithmetics in a funny variable-radix notation. We can disagree on the utility of floor division as it is currently defined, but we cannot remove or change it in an incompatible way. | ||
msg194287 - (view) | Author: Alexander Belopolsky (belopolsky) * ![]() |
Date: 2013-08-03 18:35 |
> I just want `timedelta / int` to do the same thing in Python 2.7 > with __future__.division as `timedelta / int` does in Python 3. It other words you want to backport timedelta / int true division. I am afraid it is 3-4 years too late for this request, but I will let others make a call. Count me as -0. I usually prefer explicit error over an obscure bug. If we backport true division the -Qnew flag will be changing the meaning of timedelta / int very slightly: instead of rounding down it will round to nearest. There are cases where this difference is important. Furthemore, when people compare timedeltas in tests they rarely do it with precision tolerance. If you have a good test coverage change from floor to true division may break your tests. My recommendation is: replace timedelta / int with timedelta // int in your 2.x code. | ||
msg194303 - (view) | Author: Alexander Belopolsky (belopolsky) * ![]() |
Date: 2013-08-03 21:23 |
This issue is effectively a duplicate #1083 (see .) | ||
msg227424 - (view) | Author: Antoine Pitrou (pitrou) * ![]() |
Date: 2014-09-24 08:56 |
It's not a duplicate. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:57:48 | admin | set | github: 62829 |
2015-08-28 21:25:03 | belopolsky | set | status: open -> closedresolution: wont fixstage: resolved |
2015-04-03 00:22:30 | Jean-Paul Calderone | set | nosy: - exarkun |
2014-09-24 08:56:05 | pitrou | set | status: pending -> openmessages: + |
2014-09-22 18:41:43 | serhiy.storchaka | set | status: open -> pending |
2013-08-03 21:23:28 | belopolsky | set | messages: + |
2013-08-03 18:35:51 | belopolsky | set | messages: + |
2013-08-03 18:19:22 | belopolsky | set | messages: + |
2013-08-03 17:50:54 | exarkun | set | messages: + |
2013-08-03 17:44:39 | tim.peters | set | nosy: + tim.petersmessages: + |
2013-08-03 16:43:50 | lemburg | set | messages: + |
2013-08-03 16:39:45 | mark.dickinson | set | messages: + |
2013-08-03 16:36:18 | mark.dickinson | set | messages: + |
2013-08-03 16:32:10 | belopolsky | set | messages: + |
2013-08-03 11:03:16 | mark.dickinson | set | nosy: + mark.dickinsonmessages: + |
2013-08-03 10:48:08 | lemburg | set | messages: + |
2013-08-02 22:47:52 | belopolsky | set | messages: + |
2013-08-02 22:43:31 | belopolsky | set | messages: + |
2013-08-02 21:33:12 | pitrou | set | nosy: + lemburg, belopolsky |
2013-08-02 21:27:58 | madison.may | set | nosy: + madison.maymessages: + |
2013-08-02 17:05:15 | exarkun | set | messages: + |
2013-08-02 16:21:16 | pitrou | set | nosy: + pitroumessages: + |
2013-08-02 15:22:11 | exarkun | create |