Is `if x:` completely equivalent to `if bool(x) is True:`? - python

For a small testing framework we are writing I'm trying to provide some utility functions.
One of them is supposed to be equivalent to if x: but if that is completely equivallent to if bool(x) is True: then I could only provide one function to check if x is True: and if x:.
Is the negation of that also equivalent? if bool(x) is False: equal to if not x:?

if x: is completely equivalent to testing for the boolean truth value, yes.
From the if statement documentation:
It selects exactly one of the suites by evaluating the expressions one by one until one is found to be true (see section Boolean operations for the definition of true and false)
where the Boolean operations section details how truth is defined. The bool() function follows those exact same rules:
Return a Boolean value, i.e. one of True or False. x is converted using the standard truth testing procedure.
The Standard Types documentation has a Truth Value Testing section:
Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below.
not simply inverts that same truth value; so if x is considered true by the above rules not x returns False, and True otherwise.
Be careful: in Python 2, the built-in names False and True can be masked by setting a global:
>>> True = False
>>> True
False
and thus is identity tests may be fooled by that re-assignment as well. Python 3 makes False and True keywords.
Your utility function should not need to use bool(x) is True or bool(x) is False. All you need is bool(x) and not bool(x), as these already produce True and False objects. bool() and not can't return anything else, using is True or is False on these is extremely redundant.
Last but not least, try not to re-invent the testing wheel. The Python standard library comes with a unittest library; it has both assertTrue and assertFalse functions, and the implementation of these functions just use if and if not.

Yes. These are equivalent. The rules used by if are the same as those used by bool. not simply inverts these values without changing the logic for determining truthiness or falseness.

additionally to the answer of #Martijn, if what you are building are some class, you can define what behavior you want it to have in a truth testing case like ìf x by defining __bool__ function.
>>> class A:
pass
>>> a=A()
>>> if a:
print("this instance class is truthish")
this instance class is truthish
>>> a is True
False
>>> bool(a) is True
True
>>>
the default behavior for a user defined class is to always be true in a truth test, to change that just define __bool__ (__nonzero__ in python 2) so it adjust accordingly to the semantic of the class, like for example:
>>> class Switch:
def __init__(self, state="off"):
self.state = state
def __bool__(self):
if self.state == "on":
return True
return False
>>> x=Switch()
>>> if x:
print("this instance class is truthish")
>>> x is True
False
>>> bool(x) is True
False
>>> x.state="on"
>>> if x:
print("this instance class is truthish")
this instance class is truthish
>>> x is True
False
>>> bool(x) is True
True
>>>

Related

What is the difference between assertIs and assertTrue in unittest module, when these methods are applied on True values?

My working environment:
OS: Ubuntu 18.04 (64 bits)
Python version: 3.8.0 (64 bits)
I've a question about the unittest documentation, more precisely, the difference between assertTrue and assertIs methods. Here is what the online documentation says:
https://docs.python.org/3/library/unittest.html
assertTrue(expr, msg=None)
assertFalse(expr, msg=None)
Test that expr is true (or false).
Note that this is equivalent to bool(expr) is True and not to expr is
True (use assertIs(expr, True) for the latter).
I don't really understand the difference, that is, in the case where I have a boolean type with a True value, then why should I use assertIs instead of assertTrue? Given that bool(True) is True also returns the True boolean value I don't see the difference and impact on tests. I'd appreciate if you could kindly make some clarification.
To answer the question you have to understand the difference between is and == comparison operators, imagine you have two different boys, but they are both Peters, they have the same name so == will give you true, but is will give you False, because they are not one person.
import unittest
class MyClass:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return self.value == other.value
class TestStringMethods(unittest.TestCase):
def test_your_func(self):
x = MyClass(5)
y = MyClass(5)
z = x
print(x is y) # False x is not y, they are separate object
print(x == y) # True 5 == 5
print(x is z) # True, because z is the reference to the same object
print(z == y) # True 5 == 5
# self.assertIs(x, y) # if you uncomment the line you'll get error
self.assertIs(x, z)
if __name__ == '__main__':
unittest.main()
As per AssertTruem it has absolutely different purpose to use, e.g. if []: is False and if [1] is True.
You are basically right - in most cases in tests you will probably use assertTrue for the check instead of assertIs(True).
The only really useful case where you actually need assertIs(True) would be if you want to check that the checked expression actually returns a bool value, e.g.:
def test_bool(self):
a = True
b = 1
self.assertTrue(a)
self.assertIs(a, True)
self.assertTrue(b)
self.assertIsNot(b, True)
This could be interesting if you have a function that may return values of different types, and you want to ensure that it returns a concrete bool value. Consider a (nonsense) function:
def fct(param):
if param < 0:
return False
if param > 0:
return True
return 42
...
def test_fct(self):
assertTrue(fct(1)) # will not ensure that it returns True
assertTrue(fct(0)) # also passes
assertIs(True, fct(1)) # passes as intended
assertIs(True, fct(0)) # fails, as it should
It can also help to ensure that your tested function that is supposed to return a bool value actually does that (and not just some value that would evaluate to True or False, like None or a number, due to some bug).
This all works of course because (like None), True and False are singleton objects, e.g. there is always only one of each of them.

Comparing None to several variables in Python3

Is there anyone that can tell my why I get this type of return value?
>>> a = 7
>>> b = None
>>> bool(a and b)
False
>>> bool((a is None) and (b is None))
False
>>> bool((a and b) is None) # (a and b) eq False
True
>>> bool(False is None)
False
I just can't understand why this is happening.
To explain
>>> 7 and None
None
>>> bool(None)
False
So to answer:
a and b gives None and not False as you put in comment.
bool(a and b) gives False
So then when you replace a and b by its real value:
you get bool(None is None) which is True.
I believe you had bool(bool(a and b) is None) in mind, which would give False
Python's and actually returns the last value tested. For x and y, if x is false, it will return x; if x is true, it will return y. For example:
>>> 0 and ''
0
>>> 0 and 'x'
0
>>> 1 and ''
''
>>> 1 and 'x'
'x'
So when you do a and b, you get None.
Source: Documentation: Boolean Operations — and, or, not
All Python builtins and most instances have implicit truth values thanks to their __bool__ magic methods. In your case, you are working with the builtin types of int and None. In pure python, the __bool__ method for an int would like something like the following:
class int(object):
...
def __bool__(self):
return self != 0 # True as long as not equal to 0
...
And for a None it would be the following:
class NoneType(object):
...
def __bool__(self):
return False # Always False
...
Whenever an operation is done that requires the variable to be treated as a boolean (and, or, not, bool casting), these __bool__ magic methods are used to get their corresponding instance's truth value.
So with this in mind, let's look at your expressions one-by-one.
>>> bool(a and b)
False
a is the integer 7, so by its __bool__ magic method, it has a positive truth value (True). b is None, and it has a negative truth value (False). When you and variables in Python, if the first and operand has a positive truth value, and will always return the second operand (the opposite behavior can be seen with or). More information on this can be found here. So when you do a and b, None is returned because a has a positive truth value. The resulting None is then casted to a bool which as shown in None's __bool__ method above, will be False.
>>> bool((a is None) and (b is None))
False
This evaluates to False as a is not None. Here, since you are using is in the statements, you are not comparing truth value, but you are checking if they are the same object. Since 7 and None are not the same instance, it results to False, causing the rest of the statement to evaluate to False.
>>> bool((a and b) is None) # (a and b) eq False (should be None)
True
Following up from the first one, a and b will return None. since None is the same instance as None, None is None evaluates to True. The casting to bool is redundant.
>>> bool(False is None)
False
Lastly, here we are once again checking if False and None are the same instance. As they are not, this evaluates to False and the casting to bool is once again redundant.
Consider these two facts:
None is false
and returns true if both variables are true.
In every case that misleads you, you did not consider these facts.

Why does an empty list evaluates to False on a while loop in Python

What is process that occurs for a while-loop to evaluate to False on an empty list ?
For instance:
a=[1, 2, 3]
while a:
a.pop()
Essentially, I want to know which method or attribute of the list object the while-loop is inspecting in order to decide wether to terminate or not.
Loops and conditionals implicitly use bool on all their conditions. The procedure is documented explicitly in the "Truth Value Testing" section of the docs. For a sequence like a list, this usually ends up being a check of the __len__ method.
bool works like this: first it tries the __bool__ method. If __bool__ is not implemented, it checks if __len__ is nonzero, and if that isn't possible, just returns True.
As with all magic method lookup, Python will only look at the class, never the instance (see Special method lookup). If your question is about how to change the behavior, you will need to subclass. Assigning a single replacement method to an instance dictionary won't work at all.
Great question! It's inspecting bool(a), which (usually) calls type(a).__bool__(a).
Python implements certain things using "magic methods". Basically, if you've got a data type defined like so:
class MyExampleDataType:
def __init__(self, val):
self.val = val
def __bool__(self):
return self.val > 20
Then this code will do what it looks like it'll do:
a = MyExampleDataType(5)
b = MyExampleDataType(30)
if a:
print("Won't print; 5 < 20")
if b:
print("Will print; 30 > 20")
For more information, see the Python Documentation: 3.3 Special Method Names.
A condition like if my_var is equivalent to if bool(my_var) and this page explains it rather nicely:
Return Value from bool()
The bool() returns:
False if the value is omitted or false
True if the value is true
The following values are considered false in Python:
None
False
Zero of any numeric type. For example, 0, 0.0, 0j
Empty sequence. For example, (), [], ''.
Empty mapping. For example, {}
objects of Classes which has bool() or len() method which returns 0 or False
All other values except these values are considered true.
You can check whether a list is empty by using the bool() function. It evaluates to False if the variable doesn't exist or in the case of a list, when it is empty.
In your case you could do this:
a=[1,2,3]
while bool(a) is True:
a.pop()
Or even easier:
a = [1,2,3]
while len(a) > 0:
print(a.pop())

Difference between 'not x' and 'x==None' in python

Can not x and x==None give different answers if x is a class instance ?
I mean how is not x evaluated if x is a class instance ?
yes it can give different answers.
x == None
will call the __eq__() method to valuate the operator and give the result implemented compared to the None singleton.
not x
will call the __nonzero__() (__bool__() in python3) method to valuate the operator. The interpreter will convert x to a boolean (bool(x)) using the mentioned method and then inverse its returned value because of the not operator.
x is None
means that the reference x points to the None object, which is a singleton of type NoneType and will valuate false in comparaisons. The is operator tests object identity, and thus whether or not the two objects compared are the same instance of an object, and not similar objects.
class A():
def __eq__(self, other): #other receives the value None
print 'inside eq'
return True
def __nonzero__(self):
print 'inside nonzero'
return True
...
>>> x = A()
>>> x == None #calls __eq__
inside eq
True
>>> not x #calls __nonzero__
inside nonzero
False
not x is eqivalent to:
not bool(x)
Py 3.x:
>>> class A(object):
def __eq__(self, other): #other receives the value None
print ('inside eq')
return True
def __bool__(self):
print ('inside bool')
return True
...
>>> x = A()
>>> x == None #calls __eq__
inside eq
True
>>> not x #calls __bool__
inside bool
False
Yes; not uses __bool__ (in Python 3; Python 2 uses __nonzero__), and x == None can be overridden by __eq__.
(Both are shown here.)
If x is positive the not of it means negative and vice-versa.
x == None means it will only be True if x is None is True else False. Check this.
By positive I mean the if block is chosen. True is also positive.
not x is true for a wide variety of values, e.g. 0, None, "", False, [], {}, etc.
x == None is only true for the one specific value None.
If x is a class instance, then both not x and x == None will be false, but that doesn't mean that those are equivalent expressions.
Fine; that previous paragraph should read:
If x is a class instance, then both not x and x == None will be false unless someone is playing silly buggers with the class definition.

Is there any difference between "foo is None" and "foo == None"?

Is there any difference between:
if foo is None: pass
and
if foo == None: pass
The convention that I've seen in most Python code (and the code I myself write) is the former, but I recently came across code which uses the latter. None is an instance (and the only instance, IIRC) of NoneType, so it shouldn't matter, right? Are there any circumstances in which it might?
is always returns True if it compares the same object instance
Whereas == is ultimately determined by the __eq__() method
i.e.
>>> class Foo(object):
def __eq__(self, other):
return True
>>> f = Foo()
>>> f == None
True
>>> f is None
False
You may want to read this object identity and equivalence.
The statement 'is' is used for object identity, it checks if objects refer to the same instance (same address in memory).
And the '==' statement refers to equality (same value).
A word of caution:
if foo:
# do something
Is not exactly the same as:
if x is not None:
# do something
The former is a boolean value test and can evaluate to false in different contexts. There are a number of things that represent false in a boolean value tests for example empty containers, boolean values. None also evaluates to false in this situation but other things do too.
(ob1 is ob2) equal to (id(ob1) == id(ob2))
The reason foo is None is the preferred way is that you might be handling an object that defines its own __eq__, and that defines the object to be equal to None. So, always use foo is None if you need to see if it is infact None.
There is no difference because objects which are identical will of course be equal. However, PEP 8 clearly states you should use is:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
is tests for identity, not equality. For your statement foo is none, Python simply compares the memory address of objects. It means you are asking the question "Do I have two names for the same object?"
== on the other hand tests for equality as determined by the __eq__() method. It doesn't cares about identity.
In [102]: x, y, z = 2, 2, 2.0
In [103]: id(x), id(y), id(z)
Out[103]: (38641984, 38641984, 48420880)
In [104]: x is y
Out[104]: True
In [105]: x == y
Out[105]: True
In [106]: x is z
Out[106]: False
In [107]: x == z
Out[107]: True
None is a singleton operator. So None is None is always true.
In [101]: None is None
Out[101]: True
For None there shouldn't be a difference between equality (==) and identity (is). The NoneType probably returns identity for equality. Since None is the only instance you can make of NoneType (I think this is true), the two operations are the same. In the case of other types this is not always the case. For example:
list1 = [1, 2, 3]
list2 = [1, 2, 3]
if list1==list2: print "Equal"
if list1 is list2: print "Same"
This would print "Equal" since lists have a comparison operation that is not the default returning of identity.
#Jason:
I recommend using something more along the lines of
if foo:
#foo isn't None
else:
#foo is None
I don't like using "if foo:" unless foo truly represents a boolean value (i.e. 0 or 1). If foo is a string or an object or something else, "if foo:" may work, but it looks like a lazy shortcut to me. If you're checking to see if x is None, say "if x is None:".
Some more details:
The is clause actually checks if the two objects are at the same
memory location or not. i.e whether they both point to the same
memory location and have the same id.
As a consequence of 1, is ensures whether, or not, the two lexically represented objects have identical attributes (attributes-of-attributes...) or not
Instantiation of primitive types like bool, int, string(with some exception), NoneType having a same value will always be in the same memory location.
E.g.
>>> int(1) is int(1)
True
>>> str("abcd") is str("abcd")
True
>>> bool(1) is bool(2)
True
>>> bool(0) is bool(0)
True
>>> bool(0)
False
>>> bool(1)
True
And since NoneType can only have one instance of itself in the python's "look-up" table therefore the former and the latter are more of a programming style of the developer who wrote the code(maybe for consistency) rather then having any subtle logical reason to choose one over the other.
John Machin's conclusion that None is a singleton is a conclusion bolstered by this code.
>>> x = None
>>> y = None
>>> x == y
True
>>> x is y
True
>>>
Since None is a singleton, x == None and x is None would have the same result. However, in my aesthetical opinion, x == None is best.
a is b # returns true if they a and b are true alias
a == b # returns true if they are true alias or they have values that are deemed equivalence
a = [1,3,4]
b = a[:] #creating copy of list
a is b # if gives false
False
a == b # gives true
True

Categories

Resources