有没有更优雅的表达方式((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))?

提问人:LetEpsilonBeLessThanZero 提问时间:10/17/2019 最后编辑:LetEpsilonBeLessThanZero 更新时间:6/17/2020 访问量:12203

问:

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

python 布尔逻辑

评论

8赞 smci 10/18/2019
取决于对象的类型:它们是整数/浮点数/字符串、任意对象还是什么?如果它们是内置类型,并且可以保持两者并按排序顺序排列,那么您可以避免第二个分支。请注意,创建集合将导致四个元素中的每一个都进行哈希处理,这可能是也可能不是微不足道的,或者对性能有影响,这完全取决于它们是哪种类型的对象。x,y, a,bx,ya,bx,y, a,b
18赞 10/18/2019
记住与你一起工作的人和你正在开发的项目类型。我在这里和那里使用一个小 Python。如果有人在这里编码其中一个答案,我完全必须用谷歌搜索发生了什么。无论如何,您的条件几乎是可读的。
4赞 Barmar 10/18/2019
我不会打扰任何替代品。它们更简洁,但不那么清晰(恕我直言),我认为都会更慢。
24赞 Tashus 10/18/2019
这是一个典型的 XYAB 问题。
6赞 smci 10/19/2019
一般来说,如果你正在处理与顺序无关的对象集合,请使用集合/字典/等。这确实是一个 XY 问题,我们需要查看它来自的更大的代码库。我同意这可能看起来很恶心,但是 1) 它的意图对于所有非 Python 程序员来说都是清晰易懂的,而不是神秘的 2) 解释器/编译器总是会很好地处理它,并且它基本上永远不会导致非性能代码,不像替代品。因此,“更优雅”也可能有严重的缺点。((x == a and y == b) or (x == b and y == a))

答:

155赞 Dani Mesejo 10/17/2019 #1

如果元素是可散列的,则可以使用集合:

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

评论

21赞 hobbs 10/18/2019
@Graham不,它没有,因为右手套装中正好有两件物品。如果两者都是 a,则没有 b,如果两者都是 b,则没有 a,并且在任何一种情况下,集合都不能相等。
13赞 user2357112 10/18/2019
另一方面,如果我们每边有三个元素,并且我们需要测试它们是否可以一对一匹配,那么集合将不起作用。.此时,您需要 或 .{1, 1, 2} == {1, 2, 2}sortedCounter
11赞 Édouard 10/18/2019
我发现这很难读(不要将“{}”读成“()”)。足以评论它 - 然后目的就失去了。
9赞 marxin 10/18/2019
我知道这是 python,但创建两个新集只是为了比较值对我来说似乎有点矫枉过正......只需定义一个函数即可执行此操作并在需要时调用。
6赞 Gloweye 10/18/2019
@marxin 一个函数甚至比 2 个简单的集合结构还要大。并且有 4 个参数的可读性较差。
62赞 Carcigenicate 10/17/2019 #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

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

评论

12赞 Brilliand 10/18/2019
元组方法看起来非常干净。我会担心使用集合对性能的影响。不过,你能做到吗?if (a, b) in ((x, y), (y, x))
20赞 Sneftel 10/18/2019
@Brilliand 如果您担心性能影响,那么 Python 语言不适合您。:-D
1赞 Matthieu M. 10/18/2019
我真的很喜欢第一种方法,两个元组比较。它很容易解析(一次一个子句),每个子句都非常简单。最重要的是,它应该相对有效。
0赞 user1717828 10/18/2019
是否有任何理由更喜欢答案中的解决方案而不是@Brilliand的元组解决方案?set
0赞 Bernhard Barker 10/19/2019
集合要求对象是可散列的,因此元组将是一个更通用的解决方案,具有较少的依赖关系。性能虽然可能通常并不重要,但在处理具有昂贵哈希和相等性检查的大型对象时,也可能看起来非常不同。
32赞 Thomas 10/17/2019 #3

元组使其更具可读性:

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

这给出了一个线索:我们正在检查序列是否等于序列,但忽略了排序。那只是设置平等!x, ya, b

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

评论

0赞 kissgyorgy 1/8/2020
,创建一个元组,而不是一个列表。so 和 是元组,与 和 相同。(x, y)(a, b)x, ya, b
0赞 Thomas 1/8/2020
我的意思是“元素的有序序列”意义上的“列表”,而不是 Python 类型的意义上的“列表”。编辑,因为这确实令人困惑。list
15赞 Nils Müller 10/17/2019 #4

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

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

    return (x, y) in test_set

评论

3赞 Édouard 10/18/2019
写成单行和列表(对于不可散列的项目),我认为这是最好的答案。(虽然我是一个完整的 Python 新手)。
1赞 Brilliand 10/19/2019
@Édouard 现在还有另一个答案,本质上就是这样(只是用元组而不是列表,反正效率更高)。
28赞 jasonharper 10/18/2019 #5

如果项目不可散列,但支持排序比较,则可以尝试:

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

评论

0赞 Carl Witthoft 10/18/2019
由于这也适用于可散列项目(对吧?),因此它是一种更全球化的解决方案。
6赞 dan04 10/19/2019
@CarlWitthoft:没有。有些类型是可散列的,但不可排序:例如。complex
25赞 lhf 10/18/2019 #6

如果这些是数字,则可以使用 .(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))

评论

2赞 Carsten S 10/19/2019
哈哈,对称多项式ftw!
4赞 Z4-tier 10/19/2019
这是正确的答案,尤其是最后一句话。保持简单,这不应该需要多个新的可迭代对象或类似的东西。对于那些想要使用集合的人,看看集合对象的实现,然后想象一下尝试在一个紧密的循环中运行它......
4赞 Z4-tier 10/19/2019
@RazvanSocol OP 没有说明数据类型是什么,这个答案确实限定了依赖于类型的解决方案。
28赞 Wagner Macedo 10/19/2019 #7

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

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

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

评论

0赞 scohe001 10/19/2019
这与之前的答案有何不同?
5赞 Brilliand 10/19/2019
@scohe001 它使用元组,而前面的答案使用一个集合。早先的回答确实考虑了这个解决方案,但拒绝将其列为建议。
23赞 a_guest 10/19/2019 #8

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

评论

6赞 robertlayton 10/20/2019
很好的答案。这里值得指出的是(对于那些以前没有对生成器做过太多事情的人来说),这是非常节省内存的,因为一次只创建一个排列,并且“in”检查将停止并在找到匹配项后立即返回 True。
2赞 Aleksi Torhamo 10/21/2019
还值得指出的是,这种方法的复杂性是;对于 11 个变量,这可能需要一秒钟以上的时间才能完成。(我发布了一个更快的方法,但它仍然需要 ,并开始在 10k 变量上花费一秒钟;因此,这似乎可以快速完成,也可以通常完成(可哈希性/可排序性),但不能两者兼而有之:P)O(N*N!)O(N^2)
11赞 Frank Hopkins 10/20/2019 #9

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

根据这些值实际表示的内容,最好的办法是将支票包装在一个带有说话名称的函数中。或者,或者另外,您可以在专用的更高类对象中对对象 x,y 和 a,b 进行建模,然后可以将其与类相等性检查方法或专用自定义函数中的比较逻辑进行比较。

5赞 Aleksi Torhamo 10/21/2019 #10

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

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

set(A) == set(B)速度很快,但仅当值是可哈希的,并且可以保证其中一个元组不包含任何重复值时才有效。(例如,正如@user2357112在@Daniel Mesejo的回答下指出的那样){1, 1, 2} == {1, 2, 2}

前面的方法可以通过使用带有计数的字典而不是集合来扩展为处理重复值:(这仍然有所有值都需要是可散列的限制,因此例如可变值将不起作用)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)不需要可散列或可排序的值,但如前所述,它具有复杂性,因此即使只有 11 个项目,也可能需要一秒钟才能完成。O(N*N!)

那么,有没有办法做到一般,但做得更快呢?为什么是,通过“手动”检查每个项目的数量是否相同:(这个的复杂性是,所以这对大输入也不好;在我的机器上,10k 个项目可能需要一秒钟以上 - 但对于较小的输入,比如 10 个项目,这与其他项目一样快)O(N^2)

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

为了获得最佳性能,可能需要先尝试基于 -的方法,如果由于不可哈希值而失败,则回退到基于 -的方法,如果由于不可排序的值而失败,则最后回退到基于 -的方法。dictsortedcount

评论

0赞 wjandrea 6/17/2020
counts等同于集合。计数器,它是在 Python 3 的 C 语言中实现的,所以希望应该更快。