That philosophy, taken to its logical conclusion, results in everything being broken forever.
That philosophy, taken to its logical conclusion, results in everything being broken forever.
I can't imagine a single application where you would need to preserve the value of hashed format strings. But specifying their value so that you can rely on them just seems to be a bad idea.
The case you might have to make to upgrade the version of the tool you are using has to take into account the risks, and something that is maintaining a high level of backwards compatibility has a much lower level of risk.
I generally eventually have to re-write to use a totally different tool for either of the middle two cases, because they just can't keep up. I would much rather make changes to my use of them based on deprecations than be forced to ditch them entirely.
(This is more a general statement - obviously refusing to make this datetime change isn't realistically going to push anybody away from Python.)
So? "The only valid type for a conditional is boolean" still works if types only apply to values. Under that principle, anything but True or False value encountered in evaluating the condition of an if statement ought to throw a TypeError, not be evaluated for truthiness. If you are expecting something else, call an explicit, use-case-appropriate function to get the right in-context truth value.
(Note, I'm not saying Python should do this, I'm explaining how the logic applies to Python without any contradiction to the "variables don't have types, values do" principle.)
> IMO every value except boolean false (and, if you insist, nil/null) should be truthy in a dynamic language.
I think that's better than what Python does (Ruby does that by default, with nil included as false, though it is IIRC possible-but-extremely-strongly-discouraged to override the default truthiness of objects so you could have classes with falsey values), and I lean toward preferring that approach, but the idea that a dynamic language would do well to just allow True and False as the only valid (non-error-producing) values for an "if" statement is not, IMO, without some merit.
Having a simple universal truthiness rule seems preferable and more in the spirit of a dynamic language.
It really depends on what language you're using. This is more or less true in Python, Ruby, and Javascript. In Lisp generic methods you can specify the type for the input variables and be guaranteed that if you are inside the method the parameters are of that specific type. For optimization reasons you can also declare to the compiler that variables are a specific type. This is really important when doing numeric computations in a tight loop. I've had 50% - 80% performance improvement by declaring types (amounting to hours of run time). I think Groovy and Clojure also allow this but I don't know much about those two.
There's also the maintenance issue though. After a few years of maintaining a fairly large Lisp project I've become pretty convinced that the only way to stay sane, at least for me, is to treat it as a statically typed language. I have asserts and check-type macros all over the place. If variables may have multiple types they can be checked against '(or type1 type2) but things do become more complicated then.
First, True and False were just the integers 1 and 0. Then they were given special values that numerically evaluated to 1 and 0, but __str__ now emitted 'True' or 'False'. They were still global variables, though, so they could be reassigned ('True, False = False, True') and cost a dictionary lookup to use. Finally, with Python 3, they (along with None) were made keywords so that such shenanigans could be stopped.
Which doesn't mean I disagree with changing the behaviour, but I doubt Linus would use that reason for that.
I never saw Linus complaining about changing undocummented and not used behaviour.
Midnight UTC, timezone EST: True
Midnight UTC, timezone CET: False
Midnight UTC, timezone CST: True
Midnight UTC, timezone WIT: False
Midnight UTC, timezone PST: True
Now the problem here is that it may well be documented but it is in fact unsafe to rely on in any context. The behavior is thus irretrievably broken, documented or not. It isn't even expert-friendly. The behavior, documented or not, is irretrievably broken. After all timezones west of GMT will never have false times.If there is a use case for this behavior, I sure can't see it. Unless you like bugs appearing when you change timezones.
Language interpreters can be annoying, but not to the same degree. It is quite easy to run multiple Python versions on one system. It's also relatively easy to find and fix the broken Python code in a case like this -- it's always sitting there on your drive, open for inspection. And it's not black magic -- anyone with a little programming experience, even if it's not in Python, can easily understand the issue and the fix, and make the appropriate change.
Nuance matters, and unlike a boolean, principles are not always binary choices.
I will give you an example that bit my project hard, caused hours upon hours of wasted time for bugfixes, broke our software, etc. and yet was a good thing: PostgreSQL 8.3's removal of most implicit type casts. Prior to PostgreSQL 8.3, the following query would be valid and evaluate to false:
select '2014-01-01' = 2014-01-01;
What would happen is the system would look for possible types to compare and find that text matched. It would then treat this as: SELECT '2014-01-01'::text = (2014 - 1 - 1)::text;
And further to: SELECT '2014-01-01'::text = '2012'::text
which was not what you meant. The team made the right decision to make this change and subsequently broke a lot of apps out there. I think it took us a couple months to be sure we had all the breakage out of LedgerSMB 1.2.x. Painful change. Broke a whole bunch of apps. But it was an important one and we are all better off for it.Now, Linux exists in an ecosystem of mostly source-compatible operating systems. People write portable software that they expect to run on FreeBSD, Linux, and so forth with minimal tweaking. I am not sure "we don't break user space ever" would be a compelling argument against fixing non-POSIX-compliant but existing and documented behavior. But this scenario (and frankly the stupidity in the midnight type handling in Python) is what pre-review is supposed to help prevent in the first place.
I started on the side of the original person but was persuaded round by the responder.
"if x:" means something completely different than "if x is not None", adn that needs to be understood. The fact that they might evaluate to the same result a lot of the time is luck, and is how people have got into this mess. Changing the boolean evaluation in this case is actually making the mess even worse.
I don't see how that's possible. Assuming that using "if x:" is wrong (for some value of wrong) here, this issue only affects people who are using it wrong anyway. So all fixing it does is make the consequences of doing bad stuff less painful. I can't see how that would hurt things. Even if you think "if x:" should never be used on non-booleans, leaving land mines in there to hurt newbie (or lazy) developers is not a good way to enforce that convention. It just creates pain.
Or more simply: whether having boolean coercion for conditionals is a good idea is orthogonal to how such a coercion should work. It sounds like Python should deprecate truthiness altogether. But it hasn't, so in the meantime truthiness should work in a reasonable way, and falsy midnights are not reasonable.
Pointing out that it's documented is unhelpful (the documentation is just the wrongness restated in a different language), as is "That test does not mean what you are intending it to mean" (Tautological. The OP was suggesting changing what the test means).
Any object can be tested for truth value, for use in an if or while
condition or as operand of the Boolean operations below.
The following values are considered false:
None
False
zero of any numeric type, for example, 0, 0L, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a __nonzero__()
or __len__() method, when that method returns the integer zero or bool value False. [1]
All other values are considered true — so objects of many types are always true.
To me, that makes it clear that no valid time could be False. It isn't on that list, so it should be True. Is there any controversy about that?But it just goes entirely against the spirit of that documentation to do so. I would be fairly shocked if there are many other examples of exceptions to this rule in the standard library. That's just not how it is supposed to work. I'm primarily a python developer, and I have that list of False things very deeply internalized. They are False, other things are True. I'm sure most others devs have as well.
It is right at the top of the page on the documentation of the standard types.
I'm sure there could other classes where the truthiness had some obvious physical meaning, and objects could be either True or False in a meaninful way. But midnight is not one of those.
The issue I take with this behavior, or at least the justification for closing discussion of whether or not it's applicable (though I do agree it's not a "bug" per se, simply on the merit that it's documented--even if the documentation is unclear), is this notion that it would break code where this behavior is relied upon. First, you're assuming that someone understands the behavior clearly enough to exploit it (while there's obviously a non-zero population who aren't aware enough to avoid it). Second, you're assuming that they're not inclined to realize what an outrageously stupid idea it is to rely on midnight == False. If someone who's attempting to use truthiness as a means of determining if a time object (say, from a database) is None or falsey isn't aware of this behavior enough to not get bitten by it, what makes us think that the percentage of people who would be exploiting this behavior is somewhat higher? It's insanity. More so when each of the examples presented in the discussion for how it might be used are outrageous edge cases.
Now, would it be possible to workaround this by using datetimes, which as far as I can tell cannot be made falsey, then extract the time component later when needed after validating that the datetime is indeed not None? I can't think of any real world circumstances where you're not going to need to be aware of the date, timezone, and therefore DST when extracting times except for profiling or one-off quicky applications that don't need TZ awareness.
in Boolean contexts, a time object is considered to be true if and only if, after converting it to minutes and subtracting utcoffset() (or 0 if that’s None), the result is non-zero.
But the only reason I know that is because I read a substantial part of the exchange where Mr. Paul Moore quotes it.My initial reaction was to think "How the heck would you deduce midnight from this?" and I think arguing that the behavior is fully documented from this line alone is a bit of a stretch. It's true that it is documented, but not in a manner that is immediately obvious. Worse, there are 87 instances of "None" on that page, which makes searching for a specific issue somewhat daunting.
The irony (in terms of an unexpected side effect) is that by having the discussion they did, even if nothing comes of it in terms of fixes or changes, future developers bit by this behavior will be able to readily find it via a search for midnight, None values from time objects, etc.
[1] http://docs.python.org/3.4/library/datetime.html#time-object...
Edit: Left off the citation. Sorry. ;)
if var:
...
is better code (for my value of better, which is subjective) than: if var is not None:
...Testing for "not None" also immediately tells other devs at least something about what's going on. Example: If var is an argument to a function and you see "if var," really understanding what's going on means looking at all invocations of that function to see what's being passed in.
Further, in that scenario, it's possible that different types are being passed for that argument. While "if var" could make this work where "if var is not None" would break functionality, in my opinion, it should break. Without getting into arguments about static typing, "truthiness" allows both sloppy code and the accidental introduction of bugs that might otherwise be caught during development due to faulty logic from vague conditional checks.
I suppose the hilarious way to argue it would be to say that there is an implicit "all other things being equal" in front of each of those.
All other things being equal, I see no reason to write code that needlessly introduces potential bugs and maintainability issues by ignoring the rule of thumb that explicit trumps implicit.
The documentation is pretty clear about it:
http://docs.python.org/release/3.3.4/library/stdtypes.html#t...
Which leads to this:
>>> a=object()
>>> bool(a)
True
>>> if var in (None, 0, "", [], {}, ...):
pass
something like that?In general, most of those false-y values make sense. Midnight being a false-y value does not make sense. This seems to be a case of "we represent midnight as zero internally, and zero is false-y, so midnight should be false-y." This logic does not make sense to me. If they represented noon as 0 instead, should that evaluate to false, just because?
I agree that midnight evaluating to false is ridiculous (I wasn't aware of this, thankfully I've never run into it). I'm not sure where you got the idea that I support this, but I don't.
def __bool__(self):
if self.second or self.microsecond:
return True
offset = self.utcoffset() or timedelta(0)
return timedelta(hours=self.hour, minutes=self.minute) != offset
http://hg.python.org/cpython/file/302c8fdb17e3/Lib/datetime....Now that I look at the actual code, it makes less sense. It's not just midnight that evaluates to False. It's midnight UTC that evals to False. So if your timezone is PST, then 8AM evals to False. Since most datetime.time() objects are timezone 'dumb' by default, this distinction doesn't matter so much, but I can see this still biting some people hard, even if "Midnight is false-y" made sense.
Edit: Also, to the "explicit is better than implicit" crowd, notice this line (in core libs):
if self.second or self.microsecond:
return True
It's not: if self.second != 0 or self.microsecond != 0:
return True
What could be more 'Pythonic' than the Python core libraries?Edit: s/EST/PST/ ^^;;
http://en.wikipedia.org/wiki/Principle_of_least_astonishment
self foo isNil ifTrue: [self defaultFoo]
later some implementations added a method on object to test for nil
self foo ifNil: [self defaultFoo]
if you had to test for nil before testing for a condition it would be a bit ugly.
self foo ifNil: [((self foo) isFooish) ifTrue: [self defaultFoo]]
In ruby which was developed about a bit later perhaps to late to influence pythons bool handling they got rid of this problem by treating either nil or false as Falsey and everything else as truthy.
In python everything is true except:
None
False
zero of any numeric type, for example, 0, 0L, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a __nonzero__() or __len__() method, when that method returns the integer zero or bool value False. [1]
http://docs.python.org/2/library/stdtypes.html
In python 3 __nonzero__ is __bool__
The reason for 0 being a truthy value arguably is arguably due to historical reasons. false used to be 0 and truth used to be 1, when they introduced a bool type they made it be an integer.
Some of the important python people(who are much better programmers then me) may disagree(http://stackoverflow.com/a/3175293/259130) but I think
it was a ugly stain and should have been removed in python3 along with the whole 0(of any numeric type) being falsey.The upside to python behaving like this is that in many situations you don't care what kind of falsey value you may have and you can write
if person and person.name: instead of if person != None and person.name != None and person.name != "":
and
if students and "John" in students and students[John"].age and students[John"].friends: print "John has friends" instead of if students != None and "John" in students and students[John"].age != None and len(students[John"].friends) > 0: print "John has friends"
There are downsides to this and people may have a strong personal preference for personal scripts and you can argue that the ruby way or even the smalltalk way is conceptually nicer(and more "explicit") but this is proper pythonic style and generally you should use it in python(maybe not the numerical one except when getting len of a collection)(unless you specifically depend on different code for different falsey values).
Explicit is better than implicit.
if var is not None:
...This has now proven to be false, and python is starting to add optional type annotations. It's not practical to introduce static typing more quickly - it would break too many existing programs.
The latter seems a lot less common and surprising.
You could argue that the python is badly designed in this case and that the two should be equivalent, but that's a different argument.
if var:
...
is better in some situations, but is objectively worse in many others. If you really do want anything that's "falsey" to fall into that conditional, then by all means, use it! Just be aware that `0`, `[]`, `None`, `0.0`, etc will all be treated the same.However, it's harmful in one of its most common use cases: default values.
For example, let's say you're working with a function that takes an optional argument similar to:
def foo(x, values=None):
if values is None:
values = []
You shouldn't use a mutable default argument for several reasons, so instead you make the default "None" and set it to an empty sequence. The snippet above is the standard idiom.Let's say a user mistakenly passes "values=0" instead of "values=[0]".
If you had done:
if not values:
values = []
Then the code will happily proceed with "values" being an empty list and _silently give incorrect output_ instead of raising an error a couple of lines later.You can make the (very reasonable) argument that this is all the fault of dynamic typing, and if python was just a staticly typed language, the compiler would catch all of this, but that's beside the point.
Be aware of what you're testing if you choose "if var:" instead of "if var is not None:"
I didn't argue, and certainly didn't mean to imply, that it is always better, but in the sort of case we're considering in this example, i.e. we expect a valid time value or None, It really is madness to imply a time value of midnight is "empty".
(For certain types, they do have the same behavior. In those cases, the subjective question is fine. But times are not a case where the behavior is the same.)
From PEP-8: http://legacy.python.org/dev/peps/pep-0008/
The real problem with your argument is that the poorly-styled code of people who haven't learned better yet is in production. This isn't a theoretical exercise. This is a tool used in industry, and "teaching people to style their code better" is not a valid excuse for costing industrial users of the language money.
Further, Python markets itself as a beginner-friendly language. Your attitude is the exact opposite of beginner-friendly.
Industry must bend before truth, not bend truth over itself...
Uhm maybe I'm confused but isn't that the whole design philosophy behind python? It makes bad indentation a syntax error!
No, its syntax for determining block structure is indentation. The designers didn't say "Hey lets complain about bad indentation" they said "Hey, people should be indenting their code properly anyway, so why not make that the syntax?"
Also, they didn't introduce it to an existing project, it was there from the beginning (AFAIK).
If they wanted to make people write better code they would throw an error on coercion from time to bool so that everyone would have to fix the code.
I think it's more likely just a bad habit that some people may have picked up when using PHP or JavaScript, and mistakenly continued using after moving to Python.
PEP 8 (http://legacy.python.org/dev/peps/pep-0008/#programming-reco...) is quite clear about this:
"Comparisons to singletons like None should always be done with is or is not, never the equality operators.
Also, beware of writing if x when you really mean if x is not None -- e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context!"
PEP 8 is more representative of the actual Python community's views than the mistakes of some programmers who are more accustomed to certain languages that are not Python.