Why does `if Exception` work in Python - python

In this answer https://stackoverflow.com/a/27680814/3456281, the following construct is presented
a=[1,2]
while True:
if IndexError:
print ("Stopped.")
break
print(a[2])
which actually prints "Stopped." and breaks (tested with Python 3.4.1).
Why?! Why is if IndexError even legal? Why does a[2] not raise an IndexError with no try ... except around?

All objects have a boolean value. If not otherwise defined, that boolean value is True.
So this code is simply the equivalent of doing if True; so execution reaches the break statement immediately and the print is never reached.

View the Python documentation, section Truth Value Testing under Built-in Types of The Python Standard Library. The first sentence, and the first sentence after the last bullet point answers your question.
Any object can be tested for truth value ...
and
All other values are considered true — so objects of many types are
always true.
Here's the full text of the documentation, (content in brackets, [], are added as an augmentation):
5.1. Truth Value Testing
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 __bool__() [__nonzero__() in Python 2] or __len__() method, when that method returns the integer zero or bool value False. [See Data Model, Special Method Names, section Basic Customization, of The Python Language Reference]
All other values are considered true — so objects of many types are
always true.
Operations and built-in functions that have a Boolean result always
return 0 or False for false and 1 or True for true, unless otherwise
stated. (Important exception: the Boolean operations or and and always
return one of their operands.)
Conclusion
So since Exception does not have a __bool__, __nonzero__, or __len__, (nor fall under the other conditions listed above) an Exception object will always test as True in a boolean context.

Related

Why is NotImplemented truthy in Python 3?

This question is spurred from the answers and discussions of this question. The following snippet shows the crux of the question:
>>> bool(NotImplemented)
True
The questions I have are the following:
Why was it decided that the bool value of NotImplemented should be True? It feels unpythonic.
Is there a good reason I am unaware of? The documentation seems to just say, "because it is".
Are there any examples where this is used in a reasonable manner?
Reasoning behind why I believe it's unintuitive (please disregard the lack of best practice):
>>> class A:
... def something(self):
... return NotImplemented
...
>>> a = A()
>>> a.something()
NotImplemented
>>> if a.something():
... print("this is unintuitive")
...
this is unintuitive
It seems an odd behavior that something with such a negative connotation (lack of implementation) would be considered truthy.
Relevant text from:
NotImplemented
Special value which should be returned by the binary special methods (e.g. __eq__(), __lt__(), __add__(), __rsub__(), etc.) to indicate that the operation is not implemented with respect to the other type; may be returned by the in-place binary special methods (e.g. __imul__(), __iand__(), etc.) for the same purpose. Its truth value is true.
— From the Python Docs
Edit 1
To clarify my position, I feel that NotImplemented being able to evaluate to a boolean is an anti-pattern by itself. I feel like an Exception makes more sense, but the prevailing idea is that the constant singleton was chosen for performance reasons when evaluating comparisons between different objects. I suppose I'm looking for convincing reasons as to why this is "the way" that was chosen.
By default, an object is considered truthy (bool(obj) == True) unless its class provides a way to override its truthiness. In the case of NotImplemented, no one has ever provided a compelling use-case for bool(NotImplemented) to return False, and so <class 'NotImplementedType'> has never provided an override.
As the accepted answer already explains, all classes in python are considered truthy (bool(obj) returns True) unless they specifically change that via Truth Value Testing. It makes sense in some cases to override that, like an empty list, 0, or False (see a good list here).
However there is no compelling case for NotImplemented to be falsy. It's a special value used by the interpreter, it should only be returned by special methods, and shouldn't reach regular python code.
Special value which should be returned by the binary special methods (e.g. __eq__(), __lt__(), __add__(), __rsub__(), etc.) to indicate that the operation is not implemented with respect to the other type.
Incorrectly returning NotImplemented will result in a misleading error message or the NotImplemented value being returned to Python code.
It's used by the interpreter to choose between methods, or to otherwise influence behaviour, as is the case with with the comparison operator == or bool() itself (it checks __bool__ first and then, if it returns NotImplemented, __len__).
Note that a NotImplementedError exception exists, presumably for when it actually is an error that an operation isn't implemented. In your specific example, something of class A should probably raise this exception instead.

How to see that a numpy array of zeros is not empty?

Here is my problem:
I use numpy any() function to check if my array is empty or not.
a = numpy.array([1., 2., 3.])
a.any()
#True
a = numpy.array([0., 0., 0.])
a.any()
#False
I would think that, given that 0. is a float, the any() function from numpy would return True. How can I make this happen?
What's the reason behind the fact that zeros are not considered as actual values by numpy?
I use python 2.6
What you are observing is actually expected: any() means "is there any element whose boolean value is true in this array?". Since the boolean value of 0. is false (non-zero numbers are true), it is normal that a.any() is false when the array only contains zeroes.
You can check the boolean value of any Python object with bool().
If you need to know if your array has any element, then you can test a.size (0 for no elements).
What's the reason behind the fact that zeros are not considered as actual values by numpy?
It's a general principle in Python that "falsey" means False, None, a numeric zero*, or an empty collection. See Truth Value Testing in the documentation for the exact rules.** Different languages have different rules for what counts as truthy vs. falsy***; these are Python's.
And NumPy follows that general principle. So, zeros are considered as actual values, but they're actual false values.
So, an array full of numeric zero values does not have any truthy members, therefore calling any on it will return False.
* Note that in some cases, a value that rounds to 0.0 isn't exactly zero, in which case it may be, confusingly, true. Just one more way floating point rounding errors suck… If you really need to check that the values are non-zero, check whether they're within some appropriate epsilon of zero, rather than checking exact values. NumPy has a number of useful helpers here.
** I left out the rule that custom types can decide which values are truthy or falsy by defining a __bool__ method, or various fallbacks which depend on your exact Python version. That's how things work under the hood. But for the designer of such a class, her class should try to follow the general principle; whatever it means for her values which are "zero" or "empty" or "false" or "nonexistent", that's the rule that her __bool__ method should apply.
*** In C-family languages, it's generally zeros and NULL pointers that are falsy. In Lisp-family languages, it's only the empty list or closely-related values. In Ruby and Swift, it's just false and nil. And so on. Any rule will be counter-intuitive in some cases; as long as the language and its ecosystem are consistent, that's as good as you can hope for. (If you have to use a language that isn't consistent, like PHP or JavaScript, you'll have to keep the docs handy…)

Is there only one True and one False object in Python?

I know that Python guarantees that there is only one instance of NoneType, the None object, so that you can safely use is None to test if something equals None.
Is there an equivalent guarantee for bool True and False (i.e. that there is only one instance of each)?
If not, why not?
EDIT: In particular, I've noticed that (n+0) is (0+n) gives True for n in range(-5, 257) and False otherwise. In other words, zero, the first 256 positive and the first 5 negative integers seem to be pre-cached and are not instanced again. I am guessing that that's a choice of the interpreter (CPython, in my case) and not a specification of the language. And bool derives from int, so I still have to wonder about what expectations I can have with other interpreters.
EDIT: To clarify, since this seems to have generated a lot of confusion, my intention is not to test the boolean interpretation of a value. For that I would never use is True or is False. My intention is to be able to tell apart False from everything else, in a variable that can have values of several types including empty strings, zeros, and None, and similarly for True. I'm myself an experienced programmer, of the kind who cringes when I see "if booleanvar == True".
NOTE ON DUPLICATES: The questions this one was alleged to be a duplicate of (this and this) don't answer this question; they merely state that bool is a subclass of int that differ mainly in their repr, not if True and False are guaranteed to be unique.
Also, note that it's not a question about what the names True and False are bound to, but about the instances of the class bool.
From the docs (https://docs.python.org/2/reference/datamodel.html#the-standard-type-hierarchy):
Booleans
These represent the truth values False and True. The two objects representing the values False and True are the only Boolean objects.
There are only two objects, any computation producing a boolean will produce one of those two existing objects:
>>> (1 == 1) is True
True
>>> (1 == 0) is False
True
The bool type has only two instances, True and False. Furthermore, it can't be subclassed, so there's no way to create a derived class that can have additional instances.
But even though it's guaranteed, there's seldom a good reason to rely upon it. You should usually use if x rather than if x is True, and avoid situations where you need to distinguish True from other truthy values or False from other falsy values.

boolean and type checking in python vs numpy

I ran into unexpected results in a python if clause today:
import numpy
if numpy.allclose(6.0, 6.1, rtol=0, atol=0.5):
print 'close enough' # works as expected (prints message)
if numpy.allclose(6.0, 6.1, rtol=0, atol=0.5) is True:
print 'close enough' # does NOT work as expected (prints nothing)
After some poking around (i.e., this question, and in particular this answer), I understand the cause: the type returned by numpy.allclose() is numpy.bool_ rather than plain old bool, and apparently if foo = numpy.bool_(1), then if foo will evaluate to True while if foo is True will evaluate to False. This appears to be the work of the is operator.
My questions are: why does numpy have its own boolean type, and what is best practice in light of this situation? I can get away with writing if foo: to get expected behavior in the example above, but I like the more stringent if foo is True: because it excludes things like 2 and [2] from returning True, and sometimes the explicit type check is desirable.
You're doing something which is considered an anti-pattern. Quoting PEP 8:
Don't compare boolean values to True or False using ==.
Yes: if greeting:
No: if greeting == True:
Worse: if greeting is True:
The fact that numpy wasn't designed to facilitate your non-pythonic code isn't a bug in numpy. In fact, it's a perfect example of why your personal idiom is an anti-pattern.
As PEP 8 says, using is True is even worse than == True. Why? Because you're checking object identity: not only must the result be truthy in a boolean context (which is usually all you need), and equal to the boolean True value, it has to actually be the constant True. It's hard to imagine any situation in which this is what you want.
And you specifically don't want it here:
>>> np.True_ == True
True
>>> np.True_ is True
False
So, all you're doing is explicitly making your code incompatible with numpy, and various other C extension libraries (conceivably a pure-Python library could return a custom value that's equal to True, but I don't know of any that do so).
In your particular case, there is no reason to exclude 2 and [2]. If you read the docs for numpy.allclose, it clearly isn't going to return them. But consider some other function, like many of those in the standard library that just say they evaluate to true or to false. That means they're explicitly allowed to return one of their truthy arguments, and often will do so. Why would you want to consider that false?
Finally, why would numpy, or any other C extension library, define such bool-compatible-but-not-bool types?
In general, it's because they're wrapping a C int or a C++ bool or some other such type. In numpy's case, it's wrapping a value that may be stored in a fastest-machine-word type or a single byte (maybe even a single bit in some cases) as appropriate for performance, and your code doesn't have to care which, because all representations look the same, including being truthy and equal to the True constant.
why does numpy have its own boolean type
Space and speed. Numpy stores things in compact arrays; if it can fit a boolean into a single byte it'll try. You can't easily do this with Python objects, as you have to store references which slows calculations down significantly.
I can get away with writing if foo: to get expected behavior in the example above, but I like the more stringent if foo is True: because it excludes things like 2 and [2] from returning True, and sometimes the explicit type check is desirable.
Well, don't do that.

Why is there no explicit emptyness check (for example `is Empty`) in Python

The Zen of Python says "Explicit is better than implicit". Yet the "pythonic" way to check for emptiness is using implicit booleaness:
if not some_sequence:
some_sequence.fill_sequence()
This will be true if some_sequence is an empty sequence, but also if it is None or 0.
Compare with a theoretical explicit emptiness check:
if some_sequence is Empty:
some_sequence.fill_sequence()
With some unfavorably chosen variable name the implicit booleaness to check for emptiness gets even more confusing:
if saved:
mess_up()
Compare with:
if saved is not Empty:
mess_up()
See also: "Python: What is the best way to check if a list is empty?". I find it ironic that the most voted answer claims that implicit is pythonic.
So is there a higher reason why there is no explicit emptiness check, like for example is Empty in Python?
Polymorphism in if foo: and if not foo: isn't a violation of "implicit vs explicit": it explicitly delegates to the object being checked the task of knowing whether it's true or false. What that means (and how best to check it) obviously does and must depend on the object's type, so the style guide mandates the delegation -- having application-level code arrogantly asserts it knows better than the object would be the height of folly.
Moreover, X is Whatever always, invariably means that X is exactly the same object as Whatever. Making a totally unique exception for Empty or any other specific value of Whatever would be absurd -- hard to imagine a more unPythonic approach. And "being exactly the same object" is obviously transitive -- so you could never any more have distinct empty lists, empty sets, empty dicts... congratulations, you've just designed a completely unusable and useless language, where every empty container crazily "collapses" to a single empty container object (just imagine the fun when somebody tries to mutate an empty container...?!).
The reason why there is no is Empty is astoundingly simple once you understand what the is operator does.
From the python manual:
The operators is and is not test for object identity: x is y is true
if and only if x and y are the same object. x is not y yields the
inverse truth value.
That means some_sequence is Empty checks whether some_sequence is the same object as Empty. That cannot work the way you suggested.
Consider the following example:
>>> a = []
>>> b = {}
Now let's pretend there is this is Empty construct in python:
>>> a is Empty
True
>>> b is Empty
True
But since the is operator does identity check that means that a and b are identical to Empty. That in turn must mean that a and b are identical, but they are not:
>>> a is b
False
So to answer your question "why is there no is Empty in python?": because is does identity check.
In order to have the is Empty construct you must either hack the is operator to mean something else or create some magical Empty object which somehow detects empty collections and then be identical to them.
Rather than asking why there is no is Empty you should ask why there is no builtin function isempty() which calls the special method __isempty__().
So instead of using implicit booleaness:
if saved:
mess_up()
we have explicit empty check:
if not isempty(saved):
mess_up()
where the class of saved has an __isempty__() method implemented to some sane logic.
I find that far better than using implicit booleaness for emptyness check.
Of course you can easily define your own isempty() function:
def isempty(collection):
try:
return collection.__isempty__()
except AttributeError:
# fall back to implicit booleaness but check for common pitfalls
if collection is None:
raise TypeError('None cannot be empty')
if collection is False:
raise TypeError('False cannot be empty')
if collection == 0:
raise TypeError('0 cannot be empty')
return bool(collection)
and then define an __isempty__() method which returns a boolean for all your collection classes.
I agree that sometimes if foo: isn't explicit for me when I really want to tell the reader of the code that it's emptiness I'm testing. In those cases, I use if len(foo):. Explicit enough.
I 100% agree with Alex w.r.t is Empty being unpythonic.
Consider that Lisp has been using () empty list or its symbol NIL quite some years as False and T or anything not NIL as True, but generally computation of Truth already produced some useful result that need not be reproduce if needed. Look also partition method of strings, where middle result works very nicely as while control with the non-empty is True convention.
I try generally avoid using of len as it is most times very expensive in tight loops. It is often worth while to update length value of result in program logic instead of recalculating length.
For me I would prefer Python to have False as () or [] instead of 0, but that is how it is. Then it would be more natural to use not [] as not empty. But now () is not [] is True so you could use:
emptyset = set([])
if myset == emptyset:
If you want to be explicit of the empty set case (not myset is set([]))
I myself quite like the if not myset as my commenter.
Now came to my mind that maybe this is closest to explicit not_empty:
if any(x in myset for x in myset): print "set is not empty"
and so is empty would be:
if not any(x in myset for x in myset): print "set is empty"
There is an explicit emptyness check for iterables in Python. It is spelled not. What's implicit there? not gives True when iterable is empty, and gives False when it is nonempty.
What exactly do you object to? A name? As others have told you, it's certainly better than is Empty. And it's not so ungrammatical: considering how things are usually named in Python, we might imagine a sequence called widgets, containing, surprisingly, some widgets. Then,
if not widgets:
can be read as "if there are no widgets...".
Or do you object the length? Explicit doesn't mean verbose, those are two different concepts. Python does not have addition method, it has + operator, that is completely explicit if you know the type you're applying it to. The same thing with not.

Categories