I am just starting with Python.
How to get index of integer 1
from a list if the list contains a boolean True
object before the 1
?
>>> lst = [True, False, 1, 3]
>>> lst.index(1)
0
>>> lst.index(True)
0
>>> lst.index(0)
1
I think Python considers 0
as False
and 1
as True
in the argument of the index
method. How can I get the index of integer 1
(i.e. 2
)?
Also what is the reasoning or logic behind treating boolean object this way in list? As from the solutions, I can see it is not so straightforward.
The documentation says that
Lists are mutable sequences, typically used to store collections of homogeneous items (where the precise degree of similarity will vary by application).
You shouldn't store heterogeneous data in lists.
The implementation of list.index
only performs the comparison using Py_EQ
(==
operator). In your case that comparison returns truthy value because True
and False
have values of the integers 1 and 0, respectively (the bool class is a subclass of int after all).
However, you could use generator expression and the built-in next
function (to get the first value from the generator) like this:
In [4]: next(i for i, x in enumerate(lst) if not isinstance(x, bool) and x == 1)
Out[4]: 2
Here we check if x
is an instance of bool
before comparing x
to 1.
Keep in mind that next
can raise StopIteration
, in that case it may be desired to (re-)raise ValueError
(to mimic the behavior of list.index
).
Wrapping this all in a function:
def index_same_type(it, val):
gen = (i for i, x in enumerate(it) if type(x) is type(val) and x == val)
try:
return next(gen)
except StopIteration:
raise ValueError('{!r} is not in iterable'.format(val)) from None
Some examples:
In [34]: index_same_type(lst, 1)
Out[34]: 2
In [35]: index_same_type(lst, True)
Out[35]: 0
In [37]: index_same_type(lst, 42)
ValueError: 42 is not in iterable
Booleans are integers in Python, and this is why you can use them just like any integer:
>>> 1 + True
2
>>> [1][False]
1
[this doesn't mean you should :)]
This is due to the fact that bool
is a subclass of int
, and almost always a boolean will behave just like 0 or 1 (except when it is cast to string - you will get "False"
and "True"
instead).
Here is one more idea how you can achieve what you want (however, try to rethink you logic taking into account information above):
>>> class force_int(int):
... def __eq__(self, other):
... return int(self) == other and not isinstance(other, bool)
...
>>> force_int(1) == True
False
>>> lst.index(force_int(1))
2
This code redefines int
's method, which is used to compare elements in the index
method, to ignore booleans.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With