Python things which are neither True nor False
I just found this :
a = (None,)
print (a is True)
print (a is False)
print (a == True)
print (a == False)
print (a == None)
print (a is None)
if a : print "hello"
if not a : print "goodbye"
which produces :
False
False
False
False
False
False
hello
So a neither is, nor equals True nor False, but acts as True开发者_如何学编程 in an if statement.
Why?
Update :
actually, I've just realized that this isn't as obscure as I thought. I get the same result for a=2, as well (though not for a=0 or a=1, which are considered equal to False and True respectively)
I find almost all the explanations here unhelpful, so here is another try:
The confusion here is based on that testing with "is", "==" and "if" are three different things.
- "is" tests identity, that is, if it's the same object. That is obviously not true in this case.
- "==" tests value equality, and obviously the only built in objects with the values of True and False are the object True and False (with the exception of the numbers 0 and 1, of any numeric type).
And here comes the important part:
- 'if' tests on boolean values. That means that whatever expression you give it, it will be converted to either True or False. You can make the same with bool(). And bool((None,)) will return True. The things that will evaluate to False is listed in the docs (linked to by others here)
Now maybe this is only more clear in my head, but at least I tried. :)
a
is a one-member tuple, which evaluates to True
. is
test identity of the object, therefore, you get False
in all those test. ==
test equality of the objects, therefore, you get False
again.
in if
statement a __bool__
(or __nonzero__
) used to evaluate the object, for a non-empty tuple it should return True
, therefore you get True
. hope that answers your question.
edit: the reason True
and False
are equal to 1
and 0
respectively is because bool
type implemented as a subclass of int
type.
Things in python don't have to be one of True
or False
.
When they're used as a text expression for if
/while
loops, they're converted to booleans. You can't use is
or ==
to test what they evaluate to. You use bool( thing )
>>> a = (None,)
>>> bool(a)
True
Also note:
>>> 10 == True
False
>>> 10 is True
False
>>> bool(10)
True
TL;DR:
if
and ==
are completely different operations. The if
checks the truth value of a variable while ==
compares two variables. is
also compares two variables but it compares if both reference the same object.
So it makes no sense to compare a variable with True
, False
or None
to check it's truth value.
What happens when if
is used on a variable?
In Python a check like if
implicitly gets the bool
of the argument. So
if something:
will be (under the hood) executed like:
if bool(something):
Note that you should never use the latter in your code because it's considered less pythonic and it's slower (because Python then uses two bool
s: bool(bool(something))
). Always use the if something
.
If you're interested in how it's evaluated by CPython 3.6:
Note that CPython doesn't exactly use hasattr
here. It does check if the type
of x
implements the method but without going through the __getattribute__
method (hasattr
would use that).
In Python2 the method was called __nonzero__
and not __bool__
What happens when variables are compared using ==
?
The ==
will check for equality (often also called "value equality"). However this equality check doesn't coerce the operands (unlike in other programming languages). The value equality in Python is explicitly implemented. So you can do:
>>> 1 == True # because bool subclasses int, True is equal to 1 (and False to 0)
True
>>> 1.0 == True # because float implements __eq__ with int
True
>>> 1+1j == True # because complex implements __eq__ with int
True
However ==
will default to reference comparison (is
) if the comparison isn't implemented by either operand. That's why:
>>> (None, ) == True
False
Because tuple
doesn't "support" equality with int
and vise-versa. Note that even comparing lists to tuples is "unsupported":
>>> [None] == (None, )
False
Just in case you're interested this is how CPython (3.6) implements equality (the orange arrows indicate if an operation returned the NotImplemented
constant):
That's only roughly correct because CPython also checks if the type()
of value1
or value2
implements __eq__
(without going through the __getattribute__
method!) before it's called (if it exists) or skipped (if it doesn't exist).
Note that the behavior in Python2 was significantly more lengthy (at least if the methods returned NotImplemented
) and Python 2 also supported __cmp__
,
What happens when variables are compared using is
?
is
is generally referred to as reference equality comparison operator. It only returns True
if both variables refer to exactly the same object. In general variables that hold the same value can nevertheless refer to different objects:
>>> 1 is 1. # same value, different types
False
>>> a = 500
>>> a is 500 # same value, same type, different instances
False
Note that CPython uses cached values, so sometimes variables that "should" be different instances are actually the same instance. That's why I didn't use 500 is 500
(literals with the same value in the same line are always equal) and why I couldn't use 1
as example (because CPython re-uses the values -5 to 256).
But back to your comparisons: is
compares references, that means it's not enough if both operands have the same type and value but they have to be the same reference. Given that they didn't even have the same type (you're comparing tuple
with bool
and NoneType
objects) it's impossible that is
would return True
.
Note that True
, False
and None
(and also NotImplemented
and Ellipsis
) are constants and singletons in CPython. That's not just an optimization in these cases.
(None,) is a tuple that contains an element, it's not empty and therefore does not evaluate to False in that context.
Because a=(None,)
is a tuple containing a single element None
Try again with a=None
and you will see there is a different result.
Also try a=()
which is the empty tuple. This has a truth value of false
In Python every type can be converted to bool
by using the bool()
function or the __nonzero__
method.
Examples:
- Sequences (lists, strings, ...) are converted to
False
when they are empty. - Integers are converted to
False
when they are equal to 0. - You can define this behavior in your own classes by overriding
__nonzero__()
.
[Edit]
In your code, the tuple (None,)
is converted using bool()
in the if
statements. Since it's non-empty, it evaluates to True
.
精彩评论