Pythonで2つの変数の論理xorを取得するにはどうすればよいですか?


648

Pythonで2つの変数の論理xorを取得するにはどうすればよいですか?

たとえば、文字列であると予想される2つの変数があります。それらの1つだけにTrue値が含まれていることをテストしたい(Noneまたは空の文字列ではない):

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")
if logical_xor(str1, str2):
    print "ok"
else:
    print "bad"

^オペレータは、ビット単位であるように思われ、すべてのオブジェクトに定義されていません。

>>> 1 ^ 1
0
>>> 2 ^ 1
3
>>> "abc" ^ ""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for ^: 'str' and 'str'

3
いくつかの文字列に対して「xor」をどのように定義しますか?"abc" ^ ""が返すべきではないものは何だと思いますか?
Mehrdad Afshari、

18
通常のPythonのブール型で定義されているように、文字列の1つだけがTrueであるため、例外を発生させるのではなく、Trueを返す必要があります。
Zach Hirsch、

38
Pythonには、最も直感的なPythonic実装である「xor」と呼ばれる中置演算子がないことに驚いています。「^」の使用は他の言語と一貫性がありますが、ほとんどのPythonほど露骨に読めるわけではありません。
Mark E. Haase

13
@MehrdadAfshariあなたの質問への明白な答えはそれです a xor aとして定義され(a and not b) or (not a and b)、そのためa xor b、ときab文字列、または任意の他の種類があり、何でも得られるはず(a and not b) or (not a and b)利回りを。
Kaz 2013年

1
問題は、ドキュメントが不十分であることです。^は「ビット単位の排他的論理和」であり、文字どおりに解釈されるのはビット単位であり、ブール単位ではありません。したがって、x'FFFF00 '^ x'FFFF00'はx'000000 'である必要があります。それとも、文字単位でのみ発生することを意図していますか?数字としてキャスト?長い文字列の長さに一致するように、短い文字列の文字を繰り返す必要があります。これはすべてに構築する必要があります。
mckenzm

回答:


1188

入力をブール値に既に正規化している場合、!=はxorです。

bool(a) != bool(b)

148
これは賢くて短いですが、それがきれいであるとは思いません。誰かがこの構文をコードで読んだとき、これがxor操作であることはすぐにわかりますか?私はコメントを追加する義務があると感じました-不明瞭なコードを書いていることを示すサインであり、コメントで謝罪しようとします。

47
おそらく「XORであることは明らかですか?」間違った質問です。2つの質問に対する答えが同じかどうかを確認しようとしていただけで、XORを使用してそれを実装することを考えていました。たとえば、リンゴとオレンジを比較しないようにしたい場合、「if xor(isApple(x)、isApple(y))」は「if isApple(x)!= isApple(y)」よりも本当に明確ですか?私には!
AmigoNico

106
「!=」をxorとして使用すると問題が発生します。おそらく、bool(a)!= bool(b)!= bool(c)は、bool(a)^ bool(b)^ bool(c)と同じであると予想されます。だからブールにキャストしますが、^をお勧めします。最初の例で何が起こっているかを知るには、「演算子の連鎖」を調べます。
elmo

19
@elmo:違いを指摘するための+1、および演算子の連鎖とは何かを教えるための+1!私は!=は^ほど読みにくいと言っている陣営にいます。
Mark E. Haase

13
bool(a) is not bool(b)代わりにすべきですか?
RNA

485

常にxorの定義を使用して、他の論理演算からそれを計算できます。

(a and not b) or (not a and b)

しかし、これは私には少し冗長すぎて、一見すると特に明確ではありません。それを行う別の方法は次のとおりです。

bool(a) ^ bool(b)

2つのブール値のxor演算子は論理xorです(ビット単位のintとは異なります)。boolは単なるのサブクラスなintので、これは理にかなっていますが、値0とのみを持つように実装されています1。また、ドメインが0andに制限されている場合、論理xorはビット単位のxorと同等1です。

したがって、logical_xor関数は次のように実装されます。

def logical_xor(str1, str2):
    return bool(str1) ^ bool(str2)

Python-3000メーリングリストのNick Coghlan氏の功績によるものです。


7
素晴らしい投稿ですが、パラメーターに名前を付けるあらゆる方法の中で、なぜ 'str1'と 'str2'なのでしょうか?
SingleNegationElimination 2009

1
@Tokenなぜだ。それらは非常にPythonicではないからですか?
orokusaki

1
@Zach Hirsch読みやすさのために(bではなくa)ではなく(aおよびbではなく)を使用できますか、またはxorと定義が矛盾していますか?
orokusaki

10
最初にnotsをこの(not b and a) or (not a and b)ように配置して、文字列があった場合にそれが返されるようにする必要があります。これは、関数が動作するためのpythonicの方法のようです。
rjmunro、

2
@TokenMacGuy:代わりに名前を付けるべきだと何を示唆しましたか?
user541686

180

ビット単位の排他的論理和は、operatorモジュール(^演算子と同じ)で既にPythonに組み込まれています。

from operator import xor
xor(bool(a), bool(b))  # Note: converting to bools is essential

3
これは私が必要としたものです。マルウェアを何度もリバースエンジニアリングする場合、XOR演算まで文字列が壊されます。これを使用するchr(xor(ord( "n")、0x1A))= 't'
ril3y

75
注意してください。これもビット単位です:をxor(1, 2)返します3。docstringから:xor(a, b) -- Same as a ^ b. インポートされたものoperatorはすべて、既存の組み込み中置演算子の関数形式にすぎないことに注意して ください。
askewchan 2013

5
@askewchan:bool型がオーバーロード__xor__してブール値を返します。正常に機能しますが、bool(a) ^ bool(b)まったく同じことを行うとやり過ぎになります。
Martijn Pieters

@MartijnPieters ^オペレーターは__xor__内部的に呼び出します。
Quantum7 2018

5
@ Quantum7:はい、なぜこれを私に言っているのかわかりません。私はと言うboolタイプを実装__xor__する方法をので、具体的^呼び出し、それをbool(a) ^ bool(b)うまくoperator.xor()機能しているという点で、ここでは関数を使用する必要はありません。
Martijn Pieters

43

ザックが説明し、あなたが使用することができます。

xor = bool(a) ^ bool(b)

個人的には、少し異なる方言を好みます。

xor = bool(a) + bool(b) == 1

この方言は、私が学校で学んだ論理的な作図言語からインスピレーションを得ており、「OR」は≥1(1以上)を含むボックスで示され、「XOR」はを含むボックスで示されていました。=1

これには、排他的または複数のオペランドに正しく実装できるという利点があります。

  • 「1 = a ^ b ^ c ...」は、真のオペランドの数が奇数であることを意味します。この演算子は「パリティ」です。
  • 「1 = a + b + c ...」は、1つのオペランドが真であることを意味します。これは「排他的論理和」であり、「他のものを除外する」という意味です。

12
つまり、True + True + False + True == 3、3!= 1ですが、True XOR True XOR False XOR True == Trueです。「複数のオペランドにXORを正しく実装する」ことについて詳しく説明できますか?
tzot 2009年

3
@tzot ddaaの解決策によれば、一度に2つの変数のみに加算を適用するため、この例は失敗します。ですから、それをすべて書き出す正しい方法がなければなりません(((((True + True)==1)+False)==1)+True)==1。ここでの答えは、完全に複数のオペランドに一般化されます。
12

6
また、3方向XORと2つのXORの操作順序グループ​​化セットとの間には違いがあります。したがって、3-WAY-XOR(A、B、C)はXOR(XOR(A、B)、C)と同じではありません。そして、ddaaの例は前者ですが、あなたの例は後者を想定しています。
2012年

3
@ Mr.Fあなたの説明はこの答えを実際に言い訳するものではありません。Pythonでは、あなただけ行う場合はTrue + True + False + True、あなたがやる取得3、およびTrue + True + False + True == 3バック与えTrueながらTrue + True + False + True == 1戻っていますFalse。つまり、ここでの答えは正しく一般化されません。そのためには、追加の作業を行う必要があります。一方、単純なTrue ^ True ^ False ^ True動作は期待どおりです。
jpmc26

3
@ jpmc26コメントがわかりません。加算アプローチは、正確に1つのオペランドがTrueマルチアリアXOR であることを確認する操作を一般化することを目的としています。これは、たとえばとは異なる操作ですA XOR B XOR ... XOR Z。言い換えると、加算ベースのバージョンを使用する予定の場合、サブミットしたサブミットTrue + True + False + Trueの結果FalseTrue、条件がをチェックする場合に機能する複数のであるため== 1です。
2015

26
  • Pythonの論理orA or B:戻っAた場合bool(A)True、それ以外のリターンB
  • Pythonの論理andA and B:戻っAた場合bool(A)False、それ以外のリターンB

その考え方のほとんどを維持するために、私の論理的なxorの定義は次のようになります。

def logical_xor(a, b):
    if bool(a) == bool(b):
        return False
    else:
        return a or b

その方法は、それが返すことができabまたはFalse

>>> logical_xor('this', 'that')
False
>>> logical_xor('', '')
False
>>> logical_xor('this', '')
'this'
>>> logical_xor('', 'that')
'that'

5
これは私には悪い、または少なくとも奇妙なようです。他の組み込み論理演算子は、3つの可能な値のいずれかを返しません。
Zach Hirsch、

2
@ザック・ヒルシュ:それが私が「その考え方のほとんどを維持する」と言った理由です-両方が真または偽であるとき、良い結果はありません
nosklo 09年

論理演算は論理値を返す必要があるため、2番目の「return aまたはb」は奇妙に見えるため、2番目の戻りはTrueを返す必要があります。
Denis Barmenkov、2011

9
@Denis Barmenkov:まあ、論理演算子Pythonのことに注意andしてor論理値を返しません。'foo' and 'bar'戻り値'bar'...
nosklo

6
一見すると、前の2つの答えが最良のように見えますが、考え直してみると、これが実際に唯一正しい答えです。つまりxor、組み込みandおよびと一致する実装の例を提供する唯一の答えですor。ただし、もちろん、実際の状況では、bool(a) ^ bool(b)またはa ^ baであることbがわかっている場合bool)の方がより簡潔です。
Erik Kaplun、2012年

23

私はいくつかのアプローチをテストしました not a != (not b)しましたが、最速であるように見えました。

ここにいくつかのテストがあります

%timeit not a != (not b)
10000000 loops, best of 3: 78.5 ns per loop

%timeit bool(a) != bool(b)
1000000 loops, best of 3: 343 ns per loop

%timeit not a ^ (not b)
10000000 loops, best of 3: 131 ns per loop

編集: 上記の例1と3には括弧がないため、結果は正しくありません。新しい結果+ truth()ShadowRangerの提案どおりに機能します。

%timeit  (not a) ^  (not b)   # 47 ns
%timeit  (not a) != (not b)   # 44.7 ns
%timeit truth(a) != truth(b)  # 116 ns
%timeit  bool(a) != bool(b)   # 190 ns

6
それは私の人生の100 nsです。私は戻れません;-)
Arel

4
中間のタイミングについてfrom operator import truthは、モジュールの上部で実行してテストできtruth(a) != truth(b)ます。boolCレベルの不可避オーバーヘッドの多くは(それが相当として引数を受け入れる必要がありコンストラクタたこと*args, **kwargsと解析tuple及びdict場合、それらを抽出するために)truth最適化されたパスを使用することができる(関数であること)を必要としないことのいずれかtuple、またはa dict、およびboolベースソリューションの約半分の時間で実行されます(ただし、notベースソリューションよりもさらに長くなります)。
ShadowRanger

9

やりがいのあるスレッド:

Anoderの考え...論理的な«xor»の動作を取得するために、Pythonの式(そうではない可能性があります)を試してみてください

真理値表は次のようになります。

>>> True is not True
False
>>> True is not False
True
>>> False is not True
True
>>> False is not False
False
>>>

そしてあなたの例の文字列の場合:

>>> "abc" is not  ""
True
>>> 'abc' is not 'abc' 
False
>>> 'abc' is not '' 
True
>>> '' is not 'abc' 
True
>>> '' is not '' 
False
>>> 

しかしながら; 上記で示したように、文字列はブール値ではないので、カップル文字列について引き出したい実際の動作に依存します...さらに«Dive Into Python»を実行すると、«The Peculiar Nature of "が見つかりますおよび」または「または」» http://www.diveintopython.net/power_of_introspection/and_or.html

申し訳ありませんが、私の英語は私の母国語ではありません。

よろしく。


私はそれを「完全に異なる」と読むためにも使用します。これは、一部の言語がバイナリ表現のビットごとの演算を実装し、結果のビットごとの演算のブール値を取得するために使用するためです。あなたの答えは、ブール空間を超えて拡張されているので、より「type-bulletproofed」だと思います。
ユーサー

あなたの答えがNone、Falseを比較するケースをカバーしているという事実を意味します ''異なるのは独特のものです。次に例を示します。bool(False)!= bool( '')にもかかわらず、Falseは '' "ではありません。この「厳密に異なる」というセマンティクスには、より同意します
yucer

8

Pythonにはビット単位の排他的OR演算子があります^

>>> True ^ False
True
>>> True ^ True
False
>>> False ^ True
True
>>> False ^ False
False

xor(^)を適用する前に入力をブール値に変換することで使用できます。

bool(a) ^ bool(b)

(編集-ありがとうArel)


あなたの答え^は、それがビット単位の xor(質問された質問のような論理的なxor ではない)であることを明確にする必要があります。bool(2) ^ bool(3)とは異なる答えを与えbool(2 ^ 3)ます。
Arel

1
@Arelしかし、そうではありません。 a ^ b多形です。場合abされているboolインスタンス、結果になりますbool同様に。この動作は、「ビット単位の」XORとは言えません。
Alfe

@Alfe重要な点は、値は最初にブール値にキャストする必要があるということです。Pythonのドキュメントでは^、タイプが保持されていることは興味深い点ですが、ビット単位であると定義されboolていintます。注:True ^ 2isは3、実際にビット単位であることを示しています。
Arel

@Arelはい、bool ^ intケースはすべてをint最初にキャストするようなものです。依然として、Pythonは組み込まれている^多くのビットのためにオペレータにint1ビットで表されるとするためのbool両方であるので、ビット単位が、ビット単位の単一ビットに対するXORがわずかである論理ブールためのXOR。
Alfe、

私は常にこの演算子を使用するのが嫌いですがxor、エンジニアリングの背景に由来するものだと私は理解していますが、これは本能的に数学的な力のように感じ2^3 = pow(2,3)ます。つまり、混乱を避けるために常に明示的にコメントしています。
ニコラスハミルトン

8

変数引数を使用したxorの単純なバリアントは見られず、Truth値のTrueまたはFalseに対する操作のみなので、誰でも使用できるようにここに投げます。それは他の人が指摘したように、かなり(非常に言わないでください)簡単です。

def xor(*vars):
    sum = False
    for v in vars:
        sum = sum ^ bool(v)
    return sum

そして、使い方も簡単です:

if xor(False, False, True, False):
    print "Hello World!"

これは一般化されたn-ary論理XORであるため、Trueオペランドの数が奇数の場合は常に真の値がTrueになります(正確に1つがTrueの場合だけでなく、これはn-ary XORがTrueの場合の1つにすぎません)。

したがって、オペランドの1つだけが真である場合にのみTrueであるn項述語を検索する場合は、次のように使用できます。

def isOne(*vars):
    sum = False
    for v in vars:
        if sum and v:
            return False
        else:
            sum = sum or v
    return sum

この回答を改善するには:(bool(False) is False) == TrueFalseそれらの行で使用できます。
pathunstrom 2015

7

排他的論理和は次のように定義されます

def xor( a, b ):
    return (a or b) and not (a and b)

2
これはxor( 'this'、 '')に対してTrueを返し、Pythonの方法に従うには、 'this'を返す必要があります。
nosklo 2009年

@nosklo:私ではなく、BDFLで取り上げてください。PythonはTrueを返すので、それはPythonの方法でなければなりません
S.Lott、2009年

2
私は他のpython論理演算子との一貫性を意味します-私がそうするとき、PythonはTrueを返しません( 'this'または '')、それは 'this'を返します。しかし、関数ではxor( 'this'、 '')はTrueを返します。「または」のPython組み込み関数が返すように、「this」を返す必要があります。
nosklo 2009年

10
Python andorショートサーキットを実行します。どのxor実装も短絡することはできないため、すでに矛盾があります。したがって、+のxorように動作する必要はありません。andor
tzot 2009年

7

ブール値のTrueとFalseの値の代わりに1と0を使用していることがあります。この場合、xorは次のように定義できます。

z = (x + y) % 2

次の真理値表があります。

     x
   |0|1|
  -+-+-+
  0|0|1|
y -+-+-+
  1|1|0|
  -+-+-+

7

私はこれが遅いことを知っていますが、私は考えていたので、文書化のためだけに価値があるかもしれません。多分これはうまくいくでしょう:np.abs(x-y)アイデアはそれです

  1. x = True = 1およびy = False = 0の場合、結果は| 1-0 | = 1 = Trueになります
  2. x = False = 0およびy = False = 0の場合、結果は| 0-0 | = 0 = Falseになります。
  3. x = True = 1およびy = True = 1の場合、結果は| 1-1 | = 0 = Falseになります。
  4. x = False = 0およびy = True = 1の場合、結果は| 0-1 | = 1 = Trueになります

7

シンプルで理解しやすい:

sum( (bool(a), bool(b) ) == 1

排他的な選択があなたの求めるものである場合、それは複数の引数に拡張できます:

sum( bool(x) for x in y ) % 2 == 1

1
sum(map(bool, y)) % 2 == 1
warvariuc

6

これはどう?

(not b and a) or (not a and b)

与えるa場合bはfalseが
与えるb場合はafalseで
与えるFalseそう

または、Python 2.5以降の3項式を使用します。

(False if a else b) if b else a

6

ここで提案されている実装の一部は、場合によってはオペランドの繰り返し評価を引き起こし、意図しない副作用を引き起こす可能性があるため、回避する必要があります。

そうは言っても、xorどちらかを返すか、TrueまたはFalseかなり単純な実装です。特に2つ以上のオペランドがある場合、どのオペランドを選択するかについてコンセンサスが存在しないため、可能であれば、オペランドの1つを返すものはよりトリッキーです。たとえば、必要がありますxor(None, -1, [], True)返すNone[]またはFalse?私はそれぞれの答えが最も直感的なものとして何人かの人々に見えるに違いない。

TrueまたはFalseの結果の場合、最大5つの選択肢があります。最初のオペランドを返す(最終結果と値が一致する場合はブール値)、最初の一致を返す(少なくとも1つが存在する場合はブール値)、最後のオペランドを返す(if ... else ...)、最後の一致を返す(if ... else ...)、または常にブール値を返します。要するに、これは5 ** 2 = 25フレーバーですxor

def xor(*operands, falsechoice = -2, truechoice = -2):
  """A single-evaluation, multi-operand, full-choice xor implementation
  falsechoice, truechoice: 0 = always bool, +/-1 = first/last operand, +/-2 = first/last match"""
  if not operands:
    raise TypeError('at least one operand expected')
  choices = [falsechoice, truechoice]
  matches = {}
  result = False
  first = True
  value = choice = None
  # avoid using index or slice since operands may be an infinite iterator
  for operand in operands:
    # evaluate each operand once only so as to avoid unintended side effects
    value = bool(operand)
    # the actual xor operation
    result ^= value
    # choice for the current operand, which may or may not match end result
    choice = choices[value]
    # if choice is last match;
    # or last operand and the current operand, in case it is last, matches result;
    # or first operand and the current operand is indeed first;
    # or first match and there hasn't been a match so far
    if choice < -1 or (choice == -1 and value == result) or (choice == 1 and first) or (choice > 1 and value not in matches):
      # store the current operand
      matches[value] = operand
    # next operand will no longer be first
    first = False
  # if choice for result is last operand, but they mismatch
  if (choices[result] == -1) and (result != value):
    return result
  else:
    # return the stored matching operand, if existing, else result as bool
    return matches.get(result, result)

testcases = [
  (-1, None, True, {None: None}, [], 'a'),
  (None, -1, {None: None}, 'a', []),
  (None, -1, True, {None: None}, 'a', []),
  (-1, None, {None: None}, [], 'a')]
choices = {-2: 'last match', -1: 'last operand', 0: 'always bool', 1: 'first operand', 2: 'first match'}
for c in testcases:
  print(c)
  for f in sorted(choices.keys()):
    for t in sorted(choices.keys()):
      x = xor(*c, falsechoice = f, truechoice = t)
      print('f: %d (%s)\tt: %d (%s)\tx: %s' % (f, choices[f], t, choices[t], x))
  print()

5

私を含む多くの人々は、xornが変数であるn入力xor回路のように動作する関数を必要としています。(https://en.wikipedia.org/wiki/XOR_gateを参照してください)。次の単純な関数はこれを実装します。

def xor(*args):
   """
   This function accepts an arbitrary number of input arguments, returning True
   if and only if bool() evaluates to True for an odd number of the input arguments.
   """

   return bool(sum(map(bool,args)) % 2)

サンプルI / Oは次のとおりです。

In [1]: xor(False, True)
Out[1]: True

In [2]: xor(True, True)
Out[2]: False

In [3]: xor(True, True, True)
Out[3]: True

5

Pythonで2つ以上の変数の論理xorを取得するには:

  1. 入力をブール値に変換する
  2. ビットごとのxor演算子(^またはoperator.xor)を使用する

例えば、

bool(a) ^ bool(b)

入力をブール値に変換すると、ビット単位の xorは論理 xorになります。

受け入れられた答えは間違っていることに注意してください: 演算子チェーンの!=微妙さのため、Pythonのxorと同じではありません。

たとえば、以下を使用する場合、以下の3つの値のxorは間違ってい!=ます。

True ^  False ^  False  # True, as expected of XOR
True != False != False  # False! Equivalent to `(True != False) and (False != False)`

(PS私はこの警告を含めるために承認された回答を編集しようとしましたが、私の変更は拒否されました。)


4

XORの機能を知っていれば簡単です。

def logical_xor(a, b):
    return (a and not b) or (not a and b)

test_data = [
  [False, False],
  [False, True],
  [True, False],
  [True, True],
]

for a, b in test_data:
    print '%r xor %s = %r' % (a, b, logical_xor(a, b))

4

これは、2つ(またはそれ以上)の変数の論理排他XORを取得します

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")

any([str1, str2]) and not all([str1, str2])

この設定の最初の問題は、リスト全体を2回トラバースする可能性が高く、少なくとも、要素の少なくとも1つを2回チェックすることです。そのため、コードの理解度は高まるかもしれませんが、速度には向いていません(ユースケースによっては無視できる程度の違いになるかもしれません)。

このセットアップの2番目の問題は、変数の数に関係なく排他性をチェックすることです。これは最初は機能と見なされるかもしれませんが、変数の数が増えると(最初の問題はさらに大きくなります)、さらに重要になります。


4

Xorは^Pythonです。それは返します:

  • intのビット単位のxor
  • ブールの論理XOR
  • セット専用の組合
  • を実装するクラスのユーザー定義の結果__xor__
  • 文字列や辞書などの未定義の型のTypeError。

とにかく文字列で使用する場合は、キャストインboolすることで操作が明確になります(つまりを意味することもありますset(str1) ^ set(str2))。



3

これが、真理値表をコード化する方法です。特にxorの場合:

| a | b  | xor   |             |
|---|----|-------|-------------|
| T | T  | F     |             |
| T | F  | T     | a and not b |
| F | T  | T     | not a and b |
| F | F  | F     |             |

回答列のT値を見て、論理ORですべての真のケースをつなぎ合わせます。したがって、この真理値表は、ケース2または3で生成される可能性があります。したがって、

xor = lambda a, b: (a and not b) or (not a and b)

-6

次のコマンドを使用すると、2つの変数のxorを簡単に見つけることができます。

def xor(a,b):
    return a !=b

例:

xor(True、False)>>> True


1
またはxor("hey", "there")>>>
正しい
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.