(following some edits, here are some notes hopefully will help out with workaround / explanation)
Note 1 - how to turn a None into a False with not not
Use the not
operator to turn a None
into a boolean True
. Likewise, use not not
to turn that None
into a boolean False
.
In [4]: a = 1 in [] or None
In [5]: print(a)
None
In [6]: print(not a)
True
In [7]: print(not not a)
False
Note 2 - how to turn a None into a False with bool()
As per a comment below (by @tobias_k), we can actually turn a None
straight into a False
with the shorter code:
In [8]: print(bool(None))
False
Note 3 - in operator only works on iterable data type
The in
operator only works on data structure that is an iterable
. We can check if a data structure is an iterable with the hasattr()
function.
For example, a list
, tuple
, and range
are all iterables:
In [31]: hasattr([], '__iter__')
Out[31]: True
In [32]: hasattr((), '__iter__')
Out[32]: True
In [33]: hasattr(range(0), '__iter__')
Out[33]: True
Since these are iterables, the in
operator works (i.e. doesn't throw an error):
In [34]: 1 in []
Out[34]: False
In [35]: 1 in ()
Out[35]: False
In [36]: 1 in range(0)
Out[36]: False
Data types such as None
, int
, bool
are not iterables:
In [40]: hasattr(None, '__iter__')
Out[40]: False
In [41]: hasattr(1, '__iter__')
Out[41]: False
In [42]: hasattr(True, '__iter__')
Out[42]: False
In [43]: hasattr(False, '__iter__')
Out[43]: False
The in
operator would therefore throw an error against these non iterable types. e.g.
In [44]: 1 in None
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-44-71e8d29ac0d2> in <module>()
----> 1 1 in None
TypeError: argument of type 'NoneType' is not iterable
In [45]: 1 in 1
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-45-36200f7947c7> in <module>()
----> 1 1 in 1
TypeError: argument of type 'int' is not iterable
In [47]: 1 in False
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-47-0ba098e6e3c7> in <module>()
----> 1 1 in False
TypeError: argument of type 'bool' is not iterable
In [48]: 1 in True
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-48-5e7fb522bdca> in <module>()
----> 1 1 in True
TypeError: argument of type 'bool' is not iterable
Note 4 - Understanding "or" operator
See how the followings are evalulated:
1 in [] or None
-> (1 in []) or None
-> False or None
-> (short circuit to return the non-false) None
1 in [1, 2, 3] or None
-> (1 in [1, 2, 3]) or None
-> True or None
-> (short circuit to return the non-false) True
1 in None or []
-> (error!!!) or []
-> (short circuit to throw the) error!!!
1 in None or [1, 2, 3]
-> (error!!!) or [1, 2, 3]
-> (short circuit to throw the) error!!!
Note that 1 in None
always return an error. As explained in Note 3 previously, in
operator only works on iterable
, and None
is not an iterable. Hence the error. (print out the above tests with print()
to visualise it)
For the two erroneous cases you might want to consider adding some brackets to strengthen up the logic (as I've just read in @tobias_k 's answer). e.g.
- change
1 in None or []
to 1 in (None or [])
- this will evaluate to 1 in []
(bypassing the None
) and should now return False
(as expected)
- change
1 in None or [1, 2, 3]
to 1 in (None or [1, 2, 3])
- this will evaluate to 1 in [1, 2, 3]
(again, bypassing the None
) and should now return True
(as expected)