Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the index of an integer from a list if the list contains a boolean?

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.

like image 691
Captain_spack_jarrow Avatar asked Jun 15 '15 10:06

Captain_spack_jarrow


2 Answers

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
like image 87
vaultah Avatar answered Sep 23 '22 16:09

vaultah


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.

like image 32
Roman Bodnarchuk Avatar answered Sep 23 '22 16:09

Roman Bodnarchuk