Более элегантный способ выразить ((x == a и y == b) или (x == b и y == a))?

Я пытаюсь оценить ((x == a and y == b) or (x == b and y == a)) в Python, но он кажется немного многословным. Есть ли более элегантный способ?

Ответы

Ответ 1

Если элементы являются хэшируемыми, вы можете использовать наборы:

{a, b} == {y, x}

Ответ 2

Я думаю, что лучшее, что вы можете получить, это собрать их в кортежи:

if (a, b) == (x, y) or (a, b) == (y, x)

Или, может быть, обернуть это в поиске набора

if (a, b) in {(x, y), (y, x)}

Так как это было упомянуто парой комментариев, я сделал несколько таймингов, и кортежи и наборы, кажется, работают одинаково здесь, когда поиск не удался:

from timeit import timeit

x = 1
y = 2
a = 3
b = 4

>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
32.8357742

>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
31.6169182

Хотя при успешном поиске кортежи на самом деле быстрее:

x = 1
y = 2
a = 1
b = 2

>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
35.6219458

>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
27.753138700000008

Я решил использовать набор, потому что я выполняю поиск членства, и концептуально набор лучше подходит для этого варианта использования, чем кортеж. Если вы измерили существенное различие между двумя структурами в конкретном случае использования, выберите более быстрый вариант. Я не думаю, что производительность является фактором, хотя здесь.

Ответ 3

Кортежи делают его немного более читабельным:

(x, y) == (a, b) or (x, y) == (b, a)

Это дает подсказку: мы проверяем, равен ли список x, y списку a, b, но игнорируем порядок. Это просто установить равенство!

{x, y} == {a, b}

Ответ 4

Если элементы не могут быть хешируемыми, но поддерживают сравнения при сортировке, вы можете попробовать:

sorted((x, y)) == sorted((a, b))

Ответ 5

Если это цифры, вы можете использовать (x+y)==(a+b) and (x*y)==(a*b).

Если это сопоставимые предметы, вы можете использовать min(x,y)==min(a,b) and max(x,y)==max(a,b).

Но ((x == a and y == b) or (x == b and y == a)) ясен, безопасен и более общий.

Ответ 6

В качестве обобщения более чем двух переменных мы можем использовать itertools.permutations. Это вместо

(x == a and y == b and z == c) or (x == a and y == c and z == b) or ...

мы можем написать

(x, y, z) in itertools.permutations([a, b, c])

И, конечно, две переменные версии:

(x, y) in itertools.permutations([a, b])

Ответ 7

Самый элегантный способ, на мой взгляд, был бы

(x, y) in ((a, b), (b, a))

Это лучший способ, чем использовать наборы, т.е. {a, b} == {y, x}, как указано в других ответах, потому что нам не нужно думать, являются ли переменные хэшируемыми.

Ответ 8

Вы можете использовать кортежи для представления своих данных, а затем проверить их на включение, например:

def test_fun(x, y):
    test_set = {(a, b), (b, a)}

    return (x, y) in test_set

Ответ 9

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

В зависимости от того, какие значения на самом деле представляют вашу лучшую ставку, нужно обернуть чек в функцию с говорящим именем. В качестве альтернативы или дополнения вы можете моделировать объекты x, y и a, b каждый в выделенных объектах более высокого класса, которые затем можно сравнить с логикой сравнения в методе проверки на равенство классов или в специальной пользовательской функции.

Ответ 10

Если мы не ищем специфический для Python ответ

Ответ 11

Похоже, что OP касался только случая двух переменных, но, поскольку Qaru также предназначен для тех, кто ищет тот же вопрос позже, я попытаюсь рассмотреть общий случай здесь более подробно; Один предыдущий ответ уже содержит общий ответ с использованием itertools.permutations(), но этот метод приводит к сравнению O(N*N!), поскольку в каждой есть N! перестановки с элементами N. (Это было основной мотивацией для этого ответа)

Во-первых, давайте подведем итог, как некоторые методы из предыдущих ответов применимы к общему случаю, в качестве мотивации для метода, представленного здесь. Я буду использовать A для ссылки на (x, y) и B для ссылки на (a, b), которые могут быть кортежами произвольной (но равной) длины.

set(A) == set(B) работает быстро, но работает только в том случае, если значения могут быть хэшируемыми, и вы можете гарантировать, что один из кортежей не содержит повторяющихся значений. (Например, {1, 1, 2} == {1, 2, 2}, как указано @user2357112 в ответе @Daniel Mesejo)

Предыдущий метод может быть расширен для работы с дублирующимися значениями с помощью словарей со счетчиками вместо наборов: (Это по-прежнему имеет ограничение на то, что все значения должны быть хэшируемыми, поэтому, например, изменяемые значения, такие как list, не будут работать)

def counts(items):
    d = {}
    for item in items:
        d[item] = d.get(item, 0) + 1
    return d

counts(A) == counts(B)

sorted(A) == sorted(B) не требует хешируемых значений, но немного медленнее и требует вместо этого значения, которые можно заказать. (Например, complex не будет работать)

A in itertools.permutations(B) не требует хешируемых или заказываемых значений, но, как уже упоминалось, он имеет сложность O(N*N!), поэтому даже при наличии всего 11 элементов его завершение может занять более секунды.

Итак, есть ли способ быть общим, но сделать это значительно быстрее? Почему да, "вручную" проверяя, есть ли одинаковое количество каждого элемента: (Сложность этого - O(N^2), так что это также не подходит для больших входов; на моем компьютере 10 тысяч элементов могут занять более секунды - но с меньшими входными данными, такими как 10 элементов, это так же быстро, как и другие)

def unordered_eq(A, B):
    for a in A:
        if A.count(a) != B.count(a):
            return False
    return True

Чтобы получить максимальную производительность, можно сначала попробовать метод dict -based, вернуться к методу sorted -based, если это не получится из-за непредсказуемых значений, и, наконец, вернуться к count -based метод, если это тоже не удается из-за неупорядоченных значений.