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

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

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

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?


回答 0

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

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

If the elements are hashable, you could use sets:

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

回答 1

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

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

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

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.


回答 2

元组使其更具可读性:

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

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

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

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}

回答 3

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

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

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

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

回答 4

我认为,最优雅的方式是

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

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

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.


回答 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))很清楚,安全并且更笼统。

If these are numbers, you can use (x+y)==(a+b) and (x*y)==(a*b).

If these are comparable items, you can use min(x,y)==min(a,b) and max(x,y)==max(a,b).

But ((x == a and y == b) or (x == b and y == a)) is clear, safe, and more general.


回答 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])

As a generalization to more than two variables we can use itertools.permutations. That is instead of

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

we can write

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

And of course the two variable version:

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

回答 7

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

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

    return (x, y) in test_set

You can use tuples to represent your data and then check for set inclusion, like:

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

    return (x, y) in test_set

回答 8

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

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

You already got the most readable solution. There are other ways to express this, perhaps with less characters, but they are less straight-forward to read.

Depending on what the values actually represent your best bet is to wrap the check in a function with a speaking name. Alternatively or in addition, you can model the objects x,y and a,b each in dedicated higher class objects that you then can compare with the logic of the comparison in a class equality check method or a dedicated custom function.


回答 9

看来,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值失败。

It seems the OP was only concerned with the case of two variables, but since StackOverflow is also for those who search for the same question later, I’ll try to tackle the generic case here in some detail; One previous answer already contains a generic answer using itertools.permutations(), but that method leads to O(N*N!) comparisons, since there are N! permutations with N items each. (This was the main motivation for this answer)

First, let’s summarize how some of the methods in previous answers apply to the generic case, as motivation for the method presented here. I’ll be using A to refer to (x, y) and B to refer to (a, b), which can be tuples of arbitrary (but equal) length.

set(A) == set(B) is fast, but only works if the values are hashable and you can guarantee that one of the tuples doesn’t contain any duplicate values. (Eg. {1, 1, 2} == {1, 2, 2}, as pointed out by @user2357112 under @Daniel Mesejo’s answer)

The previous method can be extended to work with duplicate values by using dictionaries with counts, instead of sets: (This still has the limitation that all values need to be hashable, so eg. mutable values like list won’t work)

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) doesn’t require hashable values, but is slightly slower, and requires orderable values instead. (So eg. complex won’t work)

A in itertools.permutations(B) doesn’t require hashable or orderable values, but like already mentioned, it has O(N*N!) complexity, so even with just 11 items, it can take over a second to finish.

So, is there a way to be as general, but do it considerably faster? Why yes, by “manually” checking that there’s the same amount of each item: (The complexity of this one is O(N^2), so this isn’t good for large inputs either; On my machine, 10k items can take over a second – but with smaller inputs, like 10 items, this is just as fast as the others)

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

To get the best performance, one might want to try the dict-based method first, fall back to the sorted-based method if that fails due to unhashable values, and finally fall back to the count-based method if that too fails due to unorderable values.