4 ms·
Truthiness values are also WTF worthy (although not mentioned above). if not x: do_something() Where do_something will occur if x is None, zero, 0
by crdoconnor 11y ago
Truthiness values are also WTF worthy (although not mentioned above).
if not x:
do_something()
Where do_something will occur if x is None, zero, 0.0, a blank string or even midnight in some versions of python.
This has bitten me multiple times. It's a violation of python's explicit over implicit. It's an example of weak typing in an otherwise strongly typed language. There's really no good reason for it.
IMO, if x isn't a boolean, that line should just throw an exception. If you want to check for x being 0.0 or "", you should do x == "" or x == 0.0.
- josegonzalez 11y agoWhy not do `x == False` then? Arguing about a language not being as strongly typed when you're not being as explicit about your own types seems pretty roundabout.
- masklinn 11y agoYou want `x is False`, because `0 == False`: Python didn't originally have integers so `0` and `1` were used for `False` and `True` respectively. In order not to break existing code using equality comparisons (rather than implicit truthiness) or typechecking, when booleans were introduced `bool` was made a subclass of `int`, and `False` and `True` equal to respectively `0` and `1`. This is one of the warts which wasn't changed in Python 3.
- crdoconnor 11y ago>Why not do `x == False` then Because it's no more explicit than "not x" and because it's 5 characters longer. There's always a trade off to be made between clarity and verbosity. Judging by the number of bugs I've squashed caused by the developer either not realizing or simply forgetting that ("", 0, 0.0, midnight, False, [], {}, None) all resolve to false, I've come to believe this is one of those times where the clarity should probably take precedence. You can go to the other extreme by making everything explicitly statically typed which will make your code much more verbose (e.g. see Java). I believe that trade off isn't worth it either, despite the small increase in type safety.
- masklinn 11y ago> You can go to the other extreme by making everything explicitly statically typed which will make your code much more verbose (e.g. see Java). Java is not an example of a good static typing implementation and is not exemplar of verbosity in modern statically typed languages.
- StavrosK 11y agoI completely disagree. Your snippet above is exactly what I do whenever I want to check for falsiness. If I get a None, a 0, 0.0, "" or [], I check for it using "not x", sometimes using it for multiple return values (e.g. if I get a None or a 0 from mydict.get("param"), meaning that the value wasn't there or is 0). To me, this is the absolutely most reasonable way to check for truthiness or falsiness. If you want to check for equality or identity, use == or is.
- crdoconnor 11y agoAs a person who reads your code I'd like to know which one you were checking for. If you were checking for None or an empty list, for instance, because there is often a non-semantic difference between them. E.g. one can mean "value not provided" while the other means "there aren't any of that thing" and the behavior you want will often be different depending upon which one it is. It's this type of subtle difference that often causes obscure bugs.
- StavrosK 11y agoIf I need different behaviour between "value not provided" and "there aren't any of that thing", I would perform both checks (if x is None:/if not x:). The idiomatic way to check for an empty list is "if not mylist", but I still enjoy the fact that I can check for "if this is anything other than a thing I care about, do this" with one check. Not to mention that doing anything else would violate duck typing. You don't care if the thing passed is an empty list, you care if it's falsy. If you had a "reverse()" function that checked for an empty list and raised an exception, you'd get in trouble when someone passed an empty tuple. What you care for is whether the object is iterable or not, not whether it's a list.
- crdoconnor 11y ago>If I need different behaviour between "value not provided" and "there aren't any of that thing", I would perform both checks (if x is None:/if not x:). They will both be true if x is None. What you probably want is actually: if x is None: and if len(x) == 0:
- joshz 11y agoBoth behaviors are documented and "if not midnight" adds a fair bit of complexity, probably a good idea to know exactly what happens when you write that. [1] https://docs.python.org/2/library/stdtypes.html#truth-value-testing https://docs.python.org/2/library/stdtypes.html#truth-value-... [2] https://docs.python.org/2/library/datetime.html#time-objects https://docs.python.org/2/library/datetime.html#time-objects
- dalke 11y agoThe midnight behavior was found to be too buggy and surprising, with little to no real-world use, so has been removed from more recent Python versions.
- Veedrac 11y agoIf Python's `if` is weakly typed, Rust's `for` is weakly typed. Few would argue for the later, so I disagree that this is weak typing.