Установка понимания в Python и тестирование на членство в создаваемом наборе

Это на самом деле вопрос о семантике множественного понимания, но сначала мне нужно объяснить контекст. Я пытаюсь создать новый набор кортежей, в котором парное значение в таупе является уникальным независимо от порядка значений в паре. Упрощение моей реальной программы, что у меня есть что-то вроде {(1, 2), (2, 1), (3, 4)} и я хотел бы получить {(1, 2), (3, 4)}

Я пытался сделать что-то вроде этого:

oldSet = {(1, 2), (2, 1), (3, 4)}

newSet = set()
newSet = {(val1, val2) for (val1, val2) in oldSet if not (val2, val1) in newSet}

Тем не мение, newSet является {(1, 2), (2, 1), (3, 4)}, подразумевая, что что-то не так с моим условным выражением. Мое понимание понимания предполагает, что вышеизложенное является синтаксическим сахаром для чего-то вроде этого:

newSet =  set()
for (val1, val2) in oldSet:
  if not (val2, val1) in newSet:
    newSet.add((val1, val2))

Эта традиционная циклическая структура работает (newSet является {(1, 2), (3, 4)}). Есть ли что-то в понимании, которое заставляет условное оцениваться раньше? newSet есть ли участники? Я довольно новичок в Python, поэтому мне интересно, есть ли что-то тонкое, что я пропускаю.

Спасибо!

2 ответа

Решение

Ты не понял; Понимание множества - это отдельное выражение, отдельное от назначения. Выражение производит новый set() объект, который затем будет назначен newSet, заменив старый set() объект у вас был.

Таким образом, когда вы повторяете и строите набор, предыдущий и отдельный set() объект привязан к newSet остается пустым. В действительности, установленное понимание делает это:

newSet = set()
_result = set()
for (val1, val2) in oldSet:
    if not (val2, val1) in newSet:
        result.add((val1, val2))
newSet = _result

Вы можете использовать побочные эффекты, чтобы изменить отдельный набор во время итерации:

seen = set()
newSet = {(val1, val2) for (val1, val2) in oldSet
          if not ((val2, val1) in seen or seen.add((val1, val2))}

Это использует seen для отслеживания того, что уже было обработано и содержит кортеж, если оба условия выполняются:

  • обратное еще не было видно раньше,
  • seen.add() операция для кортежа возвращает ложное значение. поскольку seen.add() всегда возвращается None, это всегда будет так.

Обратите внимание, что теперь он создает один и тот же набор дважды, так что вы можете также сделать обычный цикл и покончить с этим:

newSet = set()
for (val1, val2) in oldSet:
    if not (val2, val1) in newSet:
        newSet.add((val1, val2))

Поскольку ваши кортежи состоят только из двух значений, вы также можете использовать сортировку здесь; любая пара кортежей (a, b), (b, a) имеет одну уникальную сортировку, в конце концов:

newSet = {tuple(sorted(t)) for t in oldSet}

Рабочая альтернатива:

newSet = { tuple(sorted(t)) for t in oldSet }

Ваше решение проверяет наличие кортежа в создаваемом наборе, но имя еще не связано со значением. Это будет, когда постижение закончится.

Другие вопросы по тегам