web-dev-qa-db-ja.com

((x == a and y == b)または(x == b and y == a))を表現するよりエレガントな方法はありますか?

Pythonで((x == a and y == b) or (x == b and y == a))を評価しようとしていますが、少し冗長に思えます。もっとエレガントな方法はありますか?

要素がハッシュ可能な場合、セットを使用できます。

{a, b} == {y, x}
151
Dani Mesejo

あなたが得ることができる最高のものはタプルにパッケージ化することだと思います:

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

メンバーシップルックアップを行っているため、セットを使用することを選択しました。概念的には、セットはTupleよりもそのユースケースに適しています。特定のユースケースで2つの構造間の有意差を測定した場合は、より高速な構造を使用してください。ただし、ここでパフォーマンスが要因になるとは思わない。

59
Carcigenicate

タプルを使用すると、少し読みやすくなります。

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

これは手がかりを与えます:シーケンスx, yがシーケンスa, bと等しいかどうかをチェックしていますが、順序は無視します。それは平等を設定するだけです!

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

アイテムがハッシュ可能ではないが、順序付けの比較をサポートしている場合は、次を試してください。

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

私の意見では、最もエレガントな方法は

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

これは、セットを使用するよりも優れた方法です。つまり、{a, b} == {y, x}、変数がハッシュ可能であるかどうかを考える必要がないため、他の回答に示されています。

26
Wagner Macedo

これらが数字の場合、_(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))_は明確で安全で、より一般的です。

25
lhf

3つ以上の変数の一般化として、 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])

そしてもちろん、2つの可変バージョン:

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

タプルを使用してデータを表し、次のようにセットの包含を確認できます。

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

    return (x, y) in test_set
15
Nils Müller

既に最も読みやすいソリューションを取得済み。これを表現する方法は他にもありますが、おそらく文字数は少なくなりますが、読むのは簡単ではありません。

値が実際に何を表すかによって、最善の方法は発言する名前の関数でチェックをラップするです。代わりに、または追加で、オブジェクトx、yおよびa、bをそれぞれ専用の上位クラスオブジェクトでモデル化してから、クラス等価チェックメソッドまたは専用のカスタム関数で比較のロジックと比較できます。

10
Frank Hopkins

OPは2つの変数のケースにのみ関係しているようですが、StackOverflowは後で同じ質問を検索する人にも使用されるため、ここで一般的なケースに少し詳しく取り組みます。以前の回答の1つには、itertools.permutations()を使用した一般的な回答が既に含まれていますが、それぞれNアイテムのN!順列があるため、その方法はO(N*N!)比較につながります。 (これがこの答えの主な動機でした)

まず、ここで紹介した方法の動機付けとして、以前の回答のいくつかの方法が一般的なケースにどのように適用されるかをまとめましょう。 Aを使用して(x, y)を参照し、Bを使用して(a, b)を参照します。これは、任意の(ただし等しい)長さのタプルにすることができます。

set(A) == set(B)は高速ですが、値がハッシュ可能な場合にのみ機能し、タプルの1つに重複する値が含まれないことを保証できます。 (例:{1, 1, 2} == {1, 2, 2}、@ Daniel Mesejoの回答で@ user2357112が指摘)

前の方法は、セットの代わりにカウント付きの辞書を使用することにより、重複する値で動作するように拡張できます(これには、すべての値がハッシュ可能である必要があるという制限があります。たとえば、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項目でも完了までに1秒以上かかります。

だから、一般的な方法はありますが、かなり速くなりますか?はい、なぜ「手動で」各アイテムが同じ量であることをチェックすることにより:(このアイテムの複雑さはO(N^2)なので、これは大きな入力にも適していません。私のマシンでは、10,000個のアイテムが1秒以上-ただし、10個のアイテムのような小さな入力では、これは他のアイテムと同じくらい高速です)

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

最高のパフォーマンスを得るには、まずdictベースのメソッドを試し、ハッシュできない値のために失敗した場合はsortedベースのメソッドにフォールバックし、最後にcountベースのメソッド。順序付けできない値が原因で失敗する場合。

3
Aleksi Torhamo