Is there a more elegant way to express ((x == a and y == b) or (x == b and y == a))?

I'm trying to evaluate ((x == a and y == b) or (x == b and y == a)) in Python, but it seems a bit verbose. Is there a more elegant way?


Solution 1:

If the elements are hashable, you could use sets:

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

Solution 2:

I think the best you could get is to package them into tuples:

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

Or, maybe wrap that in a set lookup

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

Just since it was mentioned by a couple comments, I did some timings, and tuples and sets appear to perform identically here when the lookup fails:

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

Although tuples are actually faster when the lookup succeeds:

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

I chose to use a set because I'm doing a membership lookup, and conceptually a set is a better fit for that use-case than a tuple. If you measured a significant different between the two structures in a particular use case, go with the faster one. I don't think performance is a factor here though.

Solution 3:

Tuples make it slightly more readable:

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

This gives a clue: we're checking whether the sequence x, y is equal to the sequence a, b but ignoring ordering. That's just set equality!

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

Solution 4:

The most elegant way, in my opinion, would be

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

This is a better way than using sets, i.e. {a, b} == {y, x}, as indicated in other answers because we don't need to think if the variables are hashable.

Solution 5:

If the items aren't hashable, but support ordering comparisons, you could try:

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