User: Password:
|
|
Subscribe / Log in / New account

A false midnight

A false midnight

Posted Mar 13, 2014 16:55 UTC (Thu) by fandingo (subscriber, #67019)
Parent article: A false midnight

The standard argument for midnight being false for datetime.Time is that it's the start of a day -- the first representable moment for that object. If we accept that argument, then the conclusion is that the same should apply to datetime.datetime and datetime.date objects. However, they do not have falsy behavior at the minimum.

>>> bool(datetime.date(datetime.MINYEAR, 1, 1))
> True
>>> bool(datetime.datetime(datetime.MINYEAR, 1, 1))
> True

That alone should be a reason to get rid of falsy behavior.


(Log in to post comments)

A false midnight

Posted Mar 13, 2014 18:08 UTC (Thu) by pboddie (guest, #50784) [Link]

The whole notion of falseness for datetimes is absurd: it's like treating every integer whose decimal representation ends with a zero as being false. I can only imagine that this "feature" came about in an ill-considered moment of "wouldn't this be cool/useful".

Time-related stuff in the Python standard library (and extensions) has been suffering for quite some time. I'm not even sure whether elementary changes were ever made to make the simpler time structures aware of time zones, which was something I looked at adding many years ago now: the whole process just stalled, bugs were shuffled around, and requests made to re-target the patches for Python 3.x, because by the time people got interested again there was no core developer interest to "enhance" Python 2.x.

It's like cross-compilation support: patches sat around for a very long time, some even got applied, but despite all this such things are "enhancements" and thus ineligible for Python 2.x even though we'd have been in bug-fixing mode had things been considered and applied in a timely fashion.

A false midnight

Posted Mar 14, 2014 1:18 UTC (Fri) by Karellen (subscriber, #67644) [Link]

"it's like treating every integer whose decimal representation ends with a zero as being false."

Or like treating every floating point value which happens to be an exact integer as false.

A false midnight

Posted Mar 14, 2014 17:37 UTC (Fri) by duelafn (subscriber, #52974) [Link]

First: I agree that it was a poor choice to make midnight False.

However, the comparison to a float where every int is false is not exactly a fair comparison since a datetime is always True. Compare instead to a Decimal implemented as a pair: (DecimalIntPart, DecimalFractionalPart). Then making the DecimalFractionalPart be False when the Decimal is an integer is much more reasonable:

if my_decimal.fractional_part: print("Is not an integer")

It, to me, makes the original implementation seem less "completely insane" and more "not the best choice".

A false midnight

Posted Mar 14, 2014 17:55 UTC (Fri) by jtc (guest, #6246) [Link]

"The standard argument for midnight being false for datetime.Time is that it's the start of a day -- the first representable moment for that object. If we accept that argument,..."

I don't think it makes sense that the start of a day (or of a Time, for that matter) should be false. So I'd recommend we not accept that argument. If the start of something can be considered false, then a Baby object, at the moment it is born (age 0) is false. The next moment it's true. How does that make sense? (Perhaps it could make sense in some contexts - that is, for some subset of possible applications; but the idea here, I believe, is to provide the most general definition possible. E.g., if a Time == 0 state in a quantum physics application makes sense as false, that doesn't mean that it would be false in a train-scheduling application.)

A false midnight

Posted Mar 14, 2014 18:17 UTC (Fri) by fandingo (subscriber, #67019) [Link]

You misunderstand my point. I don't think that anything beyond obviously False objects (numbers and empty containers) should ever implement __boot__(), and thus, should always be true.

Let me explain more why I came up with that specific argument. I read through the entire python-ideas thread, which I highly discourage anyone else from doing. The people arguing for falsey behavior fundamentally believed that midnight was a special time. There was a lot of disagreement back and forth on that subject.

Rather than attempting to walk into that line of reasoning, which seems intractable, I chose a different path that sidesteps it. So even though I don't believe it, I concede for the moment that midnight is special. That should mean that all date objects should have have this special condition at their minimum value and be False. Yet, they are always true. Thus, the rationale has been inconsistently applied and would need to be corrected, which refutes the argument that there is a compatibility, testing, and development cost to removing falsey behavior because it would need to be added to other classes.

Additionally, since months are not 0-indexed (i.e. 0-11), and datetime.MINYEAR is 1, it exposes another hole in the "midnight is special" argument. For consistency 1 CE January 1st, should be False, but that's not made up of all zeroes.

The point of my argument was that there's no need to get into the deep philosophical arguments of whether midnight is special and the wider discussion about what should be falsey. The most basic argument is that falsey behavior for datetime.time is inconsistent with the other datetime classes, and there should be consistency among them. Therefore, the whole depreciation and compatibility break has to happen anyways, and basically everyone (including Guido) agrees that if this were being redesigned falsey behavior would not be allowed.

There are a litany of other reasons that falsey behavior is bad.

A false midnight

Posted Mar 14, 2014 21:24 UTC (Fri) by jtc (guest, #6246) [Link]

Actually, I agree with your main point and wasn't meaning to argue against it. I didn't make it clear I was only disagreeing with the side issue that the start of something should evaluate to false. (And it was clear to me you weren't stating this proposition was your view.)

As far as your main point, I think your example/argument of dates never evaluating to false while times sometimes do is valid. On the other hand, someone might argue that something that can have a 0 value (such as a time) should be false when it's 0. But a date cannot really have a 0 value (since, as you implied, there is no 0th day of year 0 [disregarding whether there can actually be a year 0] and no 0th month - so you can never have a date [year/month/day] that evaluates to 0) and thus it's appropriate for a date to never evaluate to false. (This is not my argument, but one I think some people would make.)

To address that last argument, let me bring in the Baby object from my earlier post. You might have Baby == 0 (AKA baby.age == 0) and you might think of that as false. But why? What does false mean? It exists [otherwise it would be null/void and not evaluate to anything :-)] - doesn't existence imply truth?

I suppose I'm actually generalizing the argument - partly because some people will object to your argument for the reason I gave. I suggest that for a system that will be around for years and will be modified and extended, Boolean checks should be explicit (i.e., there should be no auto-conversion of a type into Boolean) - Instead of if (not currenttime), the code should read, for example: if (currenttime.is_midnight). IMO, this is clearer and much less error-prone. It's less likely that someone changing the code will make a mistake because the intent of the if ... clause wasn't clear to him. (I suppose you could say this is a long-winded way of saying what someone else suggested earlier: Make the code readable.)

I think part of the problem is the dual nature of languages such as perl and python (and, to some extent, ruby) - One purpose of these languages is to allow writing informal scripts (usually short and quick) that take care of an immediate need. The other is to develop applications, many of which will be quite large and will be around for a long time, and will be used by many people. For the first purpose it perhaps makes sense to allow, e.g., 0 => false; but for major projects I think it might not be a good idea.

A false midnight

Posted Mar 15, 2014 0:22 UTC (Sat) by fandingo (subscriber, #67019) [Link]

I pretty much entirely agree with you, so don't take these comments as a vigorous rebuttal.

> But a date cannot really have a 0 value (since, as you implied, there is no 0th day of year 0 [disregarding whether there can actually be a year 0] and no 0th month - so you can never have a date [year/month/day] that evaluates to 0) and thus it's appropriate for a date to never evaluate to false. (This is not my argument, but one I think some people would make.)

datetime.MINYEAR could be defined as 0, and I'm actually a little surprised that it is 1 because that seems incredibly arbitrary. As for having zero months and days, it's about about how you represent it. Months kind of naturally map this way if you have a list that represents the month names because 0-indexed makes that easier. Anyways, whether a month is zero-indexed or a time is tracked in a 12-hour AM/PM representation doesn't matter. That's the internal state of the object, which shouldn't leak out to users, but it does in the form of __bool__ for datetime.time.

> To address that last argument, let me bring in the Baby object from my earlier post. You might have Baby == 0 (AKA baby.age == 0) and you might think of that as false. But why?

It's obvious that some people (like Tim Peters) think this sort of behavior makes sense, but I would never write a class that did something like that. As you say, it's horribly unintuitive. If I read the line

if not my_baby:

there is a 0% that I would ever think that my_baby would be anything other than True or None (well, False if used as a sentinel but never due to __bool__). (And, I know that many people get upset of not using the pedantic "if my_baby is not None," but there's no reason for that if the object has no business being falsey.)

> It exists [otherwise it would be null/void and not evaluate to anything :-)] - doesn't existence imply truth?

I don't agree with this line of reasoning, which you go onto explain in more detail. The problem is that you're basically defining falsey behavior as NameError. That's actually an interesting thought where the sentinel None could be eliminated and a wider use of NameError used, but it would require substantial changes to Python and the development process. (You'd have to stop functions from implicitly returning None, get people to use exception handling far more, and so on.)

> Instead of if (not currenttime), the code should read, for example: if (currenttime.is_midnight). IMO, this is clearer and much less error-prone. It's less likely that someone changing the code will make a mistake because the intent of the if ... clause wasn't clear to him. (I suppose you could say this is a long-winded way of saying what someone else suggested earlier: Make the code readable.)

I take it a step further and believe that stdlib classes have no business implementing this sort of specialized behavior.

if currenttime == datetime.time(0, 0, 0):

That seems like unambiguously the correct answer. There are no surprises for the author or any subsequent readers.

> For the [informal script] purpose it perhaps makes sense to allow, e.g., 0 => false; but for major projects I think it might not be a good idea.

Yet, aren't the informal script writers the *least* likely to know about this behavior and also have the least tested code that is more likely to fall victim to unintentionally falsey behavior? Nick Coghlan said it best in the original article:

>> [...]having a caveat in your documentation isn't going to help, because people aren't even going to think to ask the question.

A false midnight

Posted Mar 20, 2014 15:54 UTC (Thu) by Wol (guest, #4433) [Link]

MINYEAR could be 0? Why?

Although the Western calendar has positive and negative years, AD and BC, it's interesting that it doesn't have a year 0. Although when you look at the history this is no surprise. The current numbering system from the (assumed) date of Jesus' birth was implemented at a time when there was no concept of 0.

Actually, this would be a perfect rational for why year 0 should be false :-)

There is no symbol in Roman Numerals for 0, and iirc, it was introduced to Western mathematicians round about 1000AD, some 600 years after the calendar was implemented.

Cheers,
Wol


Copyright © 2017, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds