Как получить индекс целого из списка, если список содержит логическое значение?

Я только начинаю с Python.

Как получить индекс integer 1 из списка, если список содержит объект boolean True до 1?

>>> lst = [True, False, 1, 3]
>>> lst.index(1)
0
>>> lst.index(True)
0
>>> lst.index(0)
1

Я думаю, что Python считает 0 как False и 1 как True в аргументе метода index. Как я могу получить индекс целых 1 (т.е. 2)?

И что же такое логика или логика для обработки булевого объекта таким образом в списке? Как из решений, я вижу, что это не так просто.

Ответы

Ответ 1

В документации говорится, что

Списки представляют собой изменяемые последовательности, обычно используемые для хранения коллекций однородные предметы (где точная степень сходства будет зависеть от приложение).

Нельзя хранить гетерогенные данные в списках. Реализация list.index выполняет только сравнение с помощью оператора Py_EQ (==). В вашем случае сравнение возвращает истинное значение, потому что True и False имеют значения целых чисел 1 и 0 соответственно (класс bool является подклассом int в конце концов).

Однако вы могли бы использовать выражение генератора и встроенную функцию next (чтобы получить первое значение от генератора), как это

In [4]: next(i for i, x in enumerate(lst) if not isinstance(x, bool) and x == 1)
Out[4]: 2

Здесь мы проверяем, является ли x экземпляром bool перед сравнением x с 1.

Имейте в виду, что next может поднять StopIteration, в этом случае может потребоваться (re) raise ValueError (для имитации поведения list.index).

Обертывание всего этого в функции:

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

Некоторые примеры:

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

Ответ 2

Булевы являются целыми числами в Python, поэтому вы можете использовать их так же, как любое целое число:

>>> 1 + True
2
>>> [1][False]
1

[это не значит, что вы должны:)]

Это связано с тем, что bool является подклассом int, и почти всегда булев будет вести себя точно так же, как 0 или 1 (кроме случаев, когда он передается в строку - вы получите "False" и "True").

Вот еще одна идея, как вы можете добиться того, чего хотите (однако, попытайтесь переосмыслить логику с учетом вышеизложенной информации):

>>> 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

Этот код переопределяет метод int, который используется для сравнения элементов в методе index, чтобы игнорировать логические значения.

Ответ 3

Вот очень простое наивное однострочное решение с использованием map и zip:

>>> zip(map(type, lst), lst).index((int, 1))
2

Здесь мы сопоставляем тип каждого элемента и создаем новый список, зашивая типы с помощью элементов и запрашивая индекс (type, value).

И вот общее итерационное решение, использующее ту же технику:

>>> from itertools import imap, izip
>>> def index(xs, x):
...     it = (i for i, (t, e) in enumerate(izip(imap(type, xs), xs)) if (t, e) == x)
...     try:
...             return next(it)
...     except StopIteration:
...             raise ValueError(x)
... 
>>> index(lst, (int, 1))
2

Здесь мы в основном делаем то же самое, но итеративно, чтобы не стоить нам многого с точки зрения эффективности памяти/пространства. Мы итератор того же выражения сверху, но используя imap и izip и создать пользовательскую функцию индекса, которая возвращает следующее значение из итератора или повышение a ValueError, если нет совпадения.

Ответ 4

Попробуйте это.

for i, j in enumerate([True, False, 1, 3]):
    if not isinstance(j, bool) and j == 1:
        print i

Вывод:

2