有没有更优雅的方式来表达((x == a and y == b) or (x == b and y == a))?

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

我正在尝试在 Python 中评估 ((x == a and y == b) or (x == b and y == a)),但它似乎有点冗长。有没有更优雅的方式?

如果元素是可哈希的,您可以使用集合:

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

我认为最好的方法是将它们打包成元组:

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

我选择使用集合是因为​​我正在进行成员查找,从概念上讲,集合比元组更适合该用例。如果您在特定用例中测量了两种结构之间的显着差异,请选择更快的结构。不过,我认为性能不是这里的一个因素。

元组使其更具可读性:

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

这提供了一个线索:我们正在检查序列 x, y 是否等于序列 a, b 但忽略了顺序。那只是设置相等!

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

您可以使用元组来表示您的数据,然后检查是否包含集合,例如:

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

    return (x, y) in test_set

如果项目不可哈希,但支持排序比较,您可以尝试:

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

如果这些是数字,你可以使用(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)) 清晰、安全且更通用。

在我看来,最优雅的方式是

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

这是比使用集合更好的方法,即 {a, b} == {y, x},如其他答案所示,因为我们不需要考虑变量是否可散列。

作为对两个以上变量的泛化,我们可以使用 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])

您已经得到了最易读的解决方案。还有其他方式可以表达这一点,也许字符较少,但阅读起来不那么直接。

根据这些值实际代表什么,您最好的选择是将支票包装在一个函数中,其中包含一个有发言权的名称。或者或另外,您可以在专用的高级 class 对象中分别对对象 x、y 和 a、b 进行建模,然后您可以将这些对象与 class 相等性检查方法或比较逻辑中的比较逻辑进行比较专用自定义功能。

OP 似乎只关注两个变量的情况,但由于 Whosebug 也适用于稍后搜索相同问题的人,因此我将尝试在此处详细解决一般情况; 已经包含使用 itertools.permutations() 的通用答案,但该方法导致 O(N*N!) 比较,因为有 N! 个排列,每个排列有 N 个项目。 (这是这个答案的主要动机)

首先,让我们总结一下之前答案中的一些方法如何应用于一般情况,作为此处介绍的方法的动机。我将使用 A 来引用 (x, y) 并使用 B 来引用 (a, b),它们可以是任意(但相等)长度的元组。

set(A) == set(B) 速度很快,但只有当值是可散列的并且您可以保证其中一个元组不包含任何重复值时才有效。 (例如 {1, 1, 2} == {1, 2, 2},正如@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 个项目,也可能需要一秒钟才能完成。

那么,有没有一种方法既通用又快得多?为什么是,通过 "manually" 检查每个项目的数量是否相同:(这个的复杂度是 O(N^2),所以这对大输入也不好;在我的机器上,10k 项目可以接管一秒钟 - 但输入较小,例如 10 个项目,这与其他项目一样快)

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

为了获得最佳性能,人们可能想先尝试基于 dict 的方法,如果由于无法散列值而失败,则回退到基于 sorted 的方法,最后失败如果由于不可排序的值也失败了,回到基于 count 的方法。