Как получить индекс целого из списка, если список содержит логическое значение?
Я только начинаю с 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