Подтвердить что ты не робот

Найти структуру кортежа, содержащую неизвестное значение внутри списка

Скажем, у меня есть список кортежей:

list = [(1,5), (1,7), (2,3)]

Есть ли способ в Python написать что-то вроде

if (1, *) in list: do things

где * означает "Я не забочусь об этом значении"? Поэтому мы проверяем, есть ли кортеж с 1 в первой позиции и с любым значением на втором.

Насколько я знаю, на других языках существуют специальные механизмы, но я просто не знаю названия этой конкретной проблемы. Так же похожее поведение в Python?

P.S.: Я знаю, что здесь могу использовать список. Меня просто интересует этот конкретный механизм.

4b9b3361

Ответ 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() велик, потому что вы можете напрямую предоставить ему функцию. Это позволяет указать определенный ключ для фильтрации и т.д. Чтобы упростить его, используйте выражение лямбда, чтобы сделать всю вещь в однострочном.