Найти структуру кортежа, содержащую неизвестное значение внутри списка
Скажем, у меня есть список кортежей:
list = [(1,5), (1,7), (2,3)]
Есть ли способ в Python написать что-то вроде
if (1, *) in list: do things
где *
означает "Я не забочусь об этом значении"? Поэтому мы проверяем, есть ли кортеж с 1
в первой позиции и с любым значением на втором.
Насколько я знаю, на других языках существуют специальные механизмы, но я просто не знаю названия этой конкретной проблемы. Так же похожее поведение в Python?
P.S.: Я знаю, что здесь могу использовать список. Меня просто интересует этот конкретный механизм.
Ответы
Ответ 1
Объект-заполнитель, как вы просите, не поддерживается изначально, но вы можете сделать что-то подобное:
class Any(object):
def __eq__(self, other):
return True
ANYTHING = Any()
lst = [(1,5), (1,7), (2,3)]
Метод __eq__
определяет, как два объекта проверяют равенство. (Подробнее см. https://docs.python.org/3/reference/datamodel.html.) Здесь ANYTHING
всегда будет проверять положительный результат на равенство с любым объектом. (Если этот объект также не переопределяет __eq__
, чтобы вернуть False.)
Оператор in
просто вызывает __eq__
для каждого элемента в вашем списке. То есть a in b
делает что-то вроде:
for elem in b:
if elem == a:
return True
Это означает, что если вы скажете (1, ANYTHING) in lst
, Python сначала сравнит (1, ANYTHING)
с первым элементом в lst
. (Tuples, в свою очередь, определяют __eq__
, чтобы вернуть True, если все его элементы __eq__
возвращают True. I.e. (x, y) == (a, b)
эквивалентно x==a and y==b
или x.__eq__(a) and y.__eq__(b)
.)
Следовательно, (1, ANYTHING) in lst
вернет True, а (3, ANYTHING) in lst
вернет False.
Также обратите внимание, что я переименовал ваш список lst
вместо list
, чтобы предотвратить столкновения имен с встроенным list
Python.
Ответ 2
Вы можете использовать функцию any()
:
if any(t[0] == 1 for t in yourlist):
Это эффективно проверяет и выходит раньше, если 1
находится в первой позиции кортежа.
Ответ 3
Не все мои методы решения, приведенные ниже, будут обязательно эффективными. Моя цель - продемонстрировать все возможные способы решения, о которых я могу думать, - в конце моего ответа я предоставляю результаты "теста", чтобы показать, почему или почему вы не должны использовать один определенный метод над другим. Я считаю, что это хороший способ обучения, и я буду бесстыдно поощрять такое обучение в своих ответах.
Подмножество + хэш set
s
>>> a_list = [(1,5), (1,7), (2,3)]
>>>
>>> set([l[0] for l in a_list])
{1, 2}
>>>
>>> 1 in set([l[0] for l in a_list])
True
map()
и анонимные функции
>>> a_list = [(1,5), (1,7), (2,3)]
>>>
>>> map(lambda x: x[0] == 1, a_list)
[True, True, False]
>>>
>>> True in set(map(lambda x: x[0] == 1, a_list))
True
filter
и анонимные функции
>>> a_list = [(1,5), (1,7), (2,3)]
>>>
>>> filter(lambda x: x[0] == 1, a_list)
[(1,5), (1,7)]
>>>
>>> len(filter(lambda x: x[0] == 1, a_list)) > 0 # non-empty list
True
MICROBENCHMARKS
Условие
- 1000 элементов
- повторение 100 тыс.
- 0-100 случайный диапазон
- Python 2.7.10, IPython 2.3.0
Script
from pprint import pprint
from random import randint
from timeit import timeit
N_ITEMS = 1000
N_SIM = 1 * (10 ** 5) # 100K = 100000
a_list = [(randint(0, 100), randint(0, 100)) for _ in range(N_ITEMS)]
set_membership_list_comprehension_time = timeit(
"1 in set([l[0] for l in a_list])",
number = N_SIM,
setup="from __main__ import a_list"
)
bool_membership_map_time = timeit(
"True in set(map(lambda x: x[0] == 1, a_list))",
number = N_SIM,
setup="from __main__ import a_list"
)
nonzero_length_filter_time = timeit(
"len(filter(lambda x: x[0] == 1, a_list)) > 0",
number = N_SIM,
setup="from __main__ import a_list"
)
any_list_comprehension_time = timeit(
"any(t[0] == 1 for t in a_list)",
number = N_SIM,
setup="from __main__ import a_list"
)
results = {
"any(t[0] == 1 for t in a_list)": any_list_comprehension_time,
"len(filter(lambda x: x[0] == 1, a_list)) > 0": nonzero_length_filter_time,
"True in set(map(lambda x: x[0] == 1, a_list))": bool_membership_map_time,
"1 in set([l[0] for l in a_list])": set_membership_list_comprehension_time
}
pprint(
sorted(results.items(), key = lambda x: x[1])
)
Результаты (в секундах)
[('any(t[0] == 1 for t in a_list)', 2.6685791015625), # winner - Martijn
('1 in set([l[0] for l in a_list])', 4.85234808921814),
('len(filter(lambda x: x[0] == 1, a_list)) > 0', 7.11224889755249),
('True in set(map(lambda x: x[0] == 1, a_list))', 10.343087911605835)]
Кто получил последний смех?... Martijn (по крайней мере, я пробовал)
MORAL OF THE STORY: не тратьте больше 10 минут, доказывая, что ваше низкое решение быстрее и эффективнее на небольших тестовых данных, когда другой пользовательский ответ является фактическим правильным.
Ответ 4
Это можно сделать в Python, используя понимание списка.
например:
a= [(1, 2), (3, 4), (4, 5), (1, 4)]
[i for i in a if i[0] == 1]
Вы получите:
[(1, 2), (1, 4)]
Ответ 5
Индексация является самой простой, но если вы хотите использовать синтаксис, аналогичный вашему примеру, где вы хотите назначить первое значение переменной и игнорировать остальные, вы можете использовать python3 расширенная итеративная распаковка.
In [3]: [a for a,*_ in l]
Out[3]: [1, 1, 2]
Или с любой логикой:
In [4]: l = [(1,5), (1,7), (2,3)]
In [5]: any(a == 1 for a,*_ in l)
Out[5]: True
Или подражать любому без вызова функции:
In [23]: l = [(1,5), (1,7), (2,3)]
In [24]: g = (a for a,*_ in l)
In [25]: 1 in g
Out[25]: True
In [26]: list(g)
Out[26]: [1, 2]
Ответ 6
Можно также обрабатывать количество элементов в кортеже.
>>> import operator
>>> mylist = [(1,2), (1,5), (4,5,8)]
>>> any(i==1 for i in map(operator.itemgetter(0), mylist))
True
Ответ 7
Похоже, вы действительно хотите filter()
, а не any()
:
tuple_list = [(1,5), (1,7), (2,3)]
for pair in filter(lambda pair: (pair[0] == 1), tuple_list):
print "Second value {pair[1]} found from {pair}".format(pair=pair)
...
Second value 5 found from (1, 5)
Second value 7 found from (1, 7)
Метод filter() велик, потому что вы можете напрямую предоставить ему функцию. Это позволяет указать определенный ключ для фильтрации и т.д. Чтобы упростить его, используйте выражение лямбда, чтобы сделать всю вещь в однострочном.