有没有更优雅的表达方式((x == a和y == b)或(x == b和y == a))?


108

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


8
取决于对象的类型x,y, a,b是:它们是整型数/浮点数/字符串,任意对象还是什么?如果它们是内置类型,并且可以同时保持它们x,ya,b排序顺序,则可以避免第二个分支。请注意,创建集合将导致对四个元素中的每一个x,y, a,b进行哈希处理,这可能是琐碎的,也可能不是琐碎的,或者对性能有影响,这完全取决于它们是什么类型的对象。
smci

18
请记住与您一起工作的人员以及您正在开发的项目的类型。我在这里和那里使用了一些Python。如果有人在这里编码了答案之一,那么我将完全必须使用Google搜索发生了什么。无论如何,您的条件几乎都是可读的。
Areks

3
我不会理会任何替代者。它们更简洁,但不清楚(恕我直言),我认为它们都会变慢。
Barmar

22
这是一个经典的XYAB问题。
塔苏斯(Tashus),

5
通常,如果要处理与顺序无关的对象集合,请使用set / dicts / etc。这确实是一个XY问题,我们需要查看它提取的更大的代码库。我同意((x == a and y == b) or (x == b and y == a))看起来有些怪异,但是1)它的意图非常清晰,所有非Python程序员(不是神秘主义者)都可以理解。2)解释器/编译器将始终能够很好地处理它,并且从根本上讲,它永远不会导致性能不佳的代码,与备择方案。因此,“更优雅”也可能有严重的弊端。
smci

Answers:


151

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

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

18
@Graham不,不是,因为在右手设置中恰好有两个项目。如果两者均为a,则不存在b,并且如果两者均为b,则不存在a,并且在两种情况下,集合均不能相等。
hobbs

12
另一方面,如果我们在每一侧都有三个元素,并且需要测试它们是否可以一对一地匹配,那么集合将不起作用。{1, 1, 2} == {1, 2, 2}。此时,您需要sortedCounter
user2357112支持Monica19年

11
我发现阅读起来很棘手(不要将“ {}”读为“()”)。足以评论它-然后失去目的。
爱德华

9
我知道它是python,但是创建两个新集只是为了比较值对我来说似乎有点过头了……只需定义一个执行此操作并在需要时调用的函数即可。
marxin

5
@marxin一个函数将比2个简单的set构造更大的杀伤力。而且4个参数的可读性较低。
Gloweye

60

我认为最好的办法是将它们包装到元组中:

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
元组方法看起来很干净。我会担心使用集合对性能的影响。你可以if (a, b) in ((x, y), (y, x))吗?
Brilliand

18
@Brilliand如果您担心性能影响,那么Python不是您想要的语言。:-D
Sneftel

我真的很喜欢第一种方法,两个元组比较。它很容易解析(一次一个子句),每个子句都很简单。最重要的是,它应该相对高效。
Matthieu M.

set在@Brilliand的元组解决方案的答案中,是否有任何理由更喜欢该解决方案?
user1717828

集合要求对象是可哈希的,因此元组将是具有较少依赖项的更通用的解决方案。尽管可能通常并不重要,但在使用昂贵的散列和相等性检查处理大型对象时,性能可能看起来也大不相同。
达克林

31

元组使其更具可读性:

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

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

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

,创建一个元组,而不是列表。所以(x, y)(a, b)是元组,与x, y和相同a, b
kissgyorgy

我的意思是“列表”是“元素的有序序列”,而不是Python list类型。之所以进行编辑,是因为确实令人困惑。
托马斯

27

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

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

由于这也适用于可哈希项(对吗?),因此它是一种更具全局性的解决方案。
卡尔·威索夫特

5
@CarlWitthoft:号但是也有一些可哈希但不可排序类型:complex,例如。
dan04 '19

26

我认为,最优雅的方式是

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

这比使用集(即{a, b} == {y, x}其他答案中指出的集)更好的方法,因为我们不需要考虑变量是否可哈希。


这与之前的答案有何不同?
scohe001

5
@ scohe001它使用一个元组,较早的答案使用一个集合。先前的答案确实考虑了此解决方案,但拒绝将其列为建议。
Brilliand

25

如果这些是数字,则可以使用(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
哈哈,对称多项式ftw!
卡斯顿S

2
我认为这会产生溢出错误的风险。
拉兹万·索科尔

3
这是正确的答案,尤其是最后一句话。保持简单,这不需要多个新的可迭代对象或类似的东西。对于那些想使用集合的人,请看一下集合对象的实现,然后想象一下如何在一个紧密的循环中运行它……
Z4层

3
@RazvanSocol OP并未说明数据类型是什么,并且此答案确实限定了取决于类型的解决方案。
Z4层

21

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

5
好答案。值得指出的是(对于那些以前没有使用生成器做过很多事情的人),这是非常高效的内存,因为一次仅创建一个排列,并且“ in”检查将停止并在a之后立即返回True。找到匹配项。
robertlayton

2
值得指出的是,这种方法的复杂性是O(N*N!):对于11个变量,这可能需要一秒钟才能完成。(我发布了一个更快的方法,但仍然需要O(N^2),并开始处理10k变量上的一秒钟;因此看来这可以快速完成,或者通常可以完成(可写性/可排序性),但不能同时使用:P)
Aleksi Torhamo

15

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

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

    return (x, y) in test_set

3
我将它写成单行并带有清单(用于非哈希项目),我认为这是最好的答案。(尽管我是一个完整的Python新手)。
爱德华

1
@Édouard现在,还有一个基本答案(就是用一个元组而不是一个列表,无论如何它都会更有效)。
Brilliand

10

您已经获得了最具可读性的解决方案。还有其他表达方式,也许用更少的字符,但是阅读起来却不那么直接。

根据值实际代表最好的选择的方式,是将支票包装在一个具有语音名称的函数中。替代地或另外,您可以在专用的高级类对象中分别对对象x,y和a,b建模,然后可以在类相等性检查方法或专用的自定义函数中将其与比较逻辑进行比较。


3

看来,OP仅关注两个变量的情况,但是由于StackOverflow也适用于以后搜索相同问题的人员,因此,我将在此处尝试更详细地讨论这种情况。先前的一个答案已经包含了使用的通用答案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个项目,这与其他项目一样快)

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

为了获得最佳的性能,人们可能会想尝试的dict第一个基于方法,回落到sorted基于方法如果失败了,由于unhashable值,最后回落到count基方法,如果太因unorderable值失败。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.