web-dev-qa-db-fra.com

Comment obtenez-vous le xor logique de deux variables en Python?

Comment obtenez-vous le xor logique de deux variables en Python?

Par exemple, j'ai deux variables que je m'attends à être des chaînes. Je veux tester qu'un seul d'entre eux contient une valeur True (n'est pas None ou la chaîne vide):

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

L'opérateur ^ semble être au niveau du bit, et non défini sur tous les objets:

>>> 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'
566
Zach Hirsch

Si vous normalisez déjà les entrées en booléens, alors! = Est xor.

bool(a) != bool(b)
1069
A. Coady

Vous pouvez toujours utiliser la définition de xor pour la calculer à partir d'autres opérations logiques:

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

Mais ceci est un peu trop verbeux pour moi et n'est pas particulièrement clair au premier abord. Une autre façon de le faire est:

bool(a) ^ bool(b)

L'opérateur xor sur deux booléens est xor logique (contrairement aux entiers, où il est au niveau des bits). Ce qui a du sens, puisque bool EST JUSTE UNE SOUS-CLASSE DE int , mais est implémenté pour n’avoir que les valeurs 0 et 1. Et xor logique est équivalent à xor au niveau du bit lorsque le domaine est limité à 0 et 1.

Ainsi, la fonction logical_xor serait implémentée comme suit:

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

Nous remercions Nick Coghlan de la liste de diffusion Python-30 .

434
Zach Hirsch

Bitwise exclusive-or est déjà intégré à Python, dans le module operator (identique au module ^ opérateur):

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

Comme Zach expliqué, vous pouvez utiliser:

xor = bool(a) ^ bool(b)

Personnellement, je privilégie un dialecte légèrement différent:

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

Ce dialecte s’inspire d’un langage logique que j’ai appris à l’école où "OR" était désigné par une boîte contenant ≥1 (supérieur ou égal à 1) et "XOR" par une boîte contenant =1.

Cela présente l’avantage d’implémenter correctement des opérandes exclusifs ou multiples.

  • "1 = a ^ b ^ c ..." signifie que le nombre d'opérandes vrais est impair. Cet opérateur est "parité".
  • "1 = a + b + c ..." signifie qu'un seul opérande est vrai. Ceci est "exclusif ou", signifiant "un à l'exclusion des autres".
38
ddaa
  • Python logique or: A or B: renvoie A si bool(A) est True, sinon renvoie B
  • Python logique and: A and B: renvoie A si bool(A) est False, sinon renvoie B

Pour conserver la plus grande partie de cette façon de penser, ma définition logique serait:

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

De cette façon, il peut retourner a, b ou False:

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

J'ai testé plusieurs approches et not a != (not b) semblait être la plus rapide.

Voici quelques tests

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

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

In [131]: %timeit not a ^ (not b)
10000000 loops, best of 3: 131 ns per loop
18
Rugnar

Fil récompensant:

Idée Anoder ... Vous venez d’essayer l’expression Pythonic (peut-être) "n’est pas" afin d’obtenir un comportement de "xor" logique

La table de vérité serait:

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

Et pour votre exemple de chaîne:

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

Pourtant; comme indiqué ci-dessus, cela dépend du comportement réel que vous souhaitez adopter à propos de quelques chaînes, car elles ne sont pas des boleans ... et plus encore: si vous "plongez au cœur de Python", vous découvrirez "La nature particulière de" et "et" ou "" http://www.diveintopython.net/power_of_introspection/and_or.html

Désolé mon anglais écrit, ce n'est pas ma langue d'origine.

Cordialement.

8
Agustin Marcos

Je sais que c'est tard, mais j'avais une idée et ça valait peut-être la peine, juste pour la documentation. Cela fonctionnerait peut-être: np.abs(x-y) L'idée est que

  1. si x = True = 1 et y = False = 0, le résultat serait | 1-0 | = 1 = True
  2. si x = False = 0 et y = False = 0, le résultat serait | 0-0 | = 0 = False
  3. si x = True = 1 et y = True = 1, le résultat serait | 1-1 | = 0 = False
  4. si x = False = 0 et y = True = 1, le résultat serait | 0-1 | = 1 = True
7
BarocliniCplusplus

Exclusive Or est défini comme suit

def xor( a, b ):
    return (a or b) and not (a and b)
7
S.Lott

Simple, facile à comprendre:

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

Si vous recherchez un choix exclusif, vous pouvez l'étendre à plusieurs arguments:

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

Comme je ne vois pas la simple variante de xor utilisant des arguments variables et uniquement une opération sur les valeurs de vérité Vrai ou Faux, je le jetterai ici pour que tout le monde l’utilise. C'est comme noté par d'autres, joli (pour ne pas dire très) simple.

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

Et l’utilisation est aussi simple:

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

Comme il s'agit du XOR logique n-aire généralisé, sa valeur de vérité sera True chaque fois que le nombre d'opérandes True sera impair (et pas seulement lorsqu'un seul est vrai, il s'agit simplement d'un cas dans lequel n-ary XOR est vrai).

Ainsi, si vous êtes à la recherche d'un prédicat n-aire qui n'est vrai que lorsque exactement l'un de ses opérandes l'est, vous voudrez peut-être utiliser:

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

Parfois, je me retrouve à travailler avec 1 et 0 au lieu de valeurs booléennes Vrai et Faux. Dans ce cas, xor peut être défini comme

z = (x + y) % 2

qui a la table de vérité suivante:

     x
   |0|1|
  -+-+-+
  0|0|1|
y -+-+-+
  1|1|0|
  -+-+-+
6
Steve L

Que dis-tu de ça?

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

donnera a si b est faux
donnera b si a est faux
donnera False sinon

Ou avec l'expression Python 2.5 + ternaire:

(False if a else b) if b else a
6
Markus Jarderot

Certaines des implémentations suggérées ici entraîneront une évaluation répétée des opérandes dans certains cas, ce qui peut entraîner des effets secondaires non souhaités et doit donc être évité.

Cela dit, une implémentation de xor qui retourne soit True ou False est assez simple; celui qui retourne l'un des opérandes, si possible, est beaucoup plus délicat, car il n'existe pas de consensus sur l'opérande choisi, en particulier lorsqu'il y a plus de deux opérandes. Par exemple, xor(None, -1, [], True) doit-il renvoyer None, [] ou False? Je parie que chaque réponse apparaît à certains comme la plus intuitive.

Il existe cinq choix possibles pour le résultat True ou le résultat False: renvoyer le premier opérande (s'il correspond au résultat final, sinon valeur booléenne), renvoyer la première correspondance (si au moins un existe, sinon valeur booléenne), retourne le dernier opérande (si ... sinon ...), retourne le dernier match (si ... autre ...), ou retourne toujours un booléen. Au total, cela fait 5 ** 2 = 25 saveurs de 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()
6
Henry 'Pi' James

C'est facile quand vous savez ce que XOR fait:

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
Denis Barmenkov

Ceci obtient l'exclusivité logique XOR pour deux (ou plus) variables

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

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

Le premier problème de cette configuration est qu’elle parcourt très probablement la totalité de la liste deux fois et vérifie au moins au moins un des éléments deux fois. Donc, cela peut augmenter la compréhension du code, mais cela ne favorise pas la vitesse (qui peut différer de manière négligeable selon votre cas d'utilisation).

Le deuxième problème de cette configuration est qu’elle vérifie l’exclusivité quel que soit le nombre de variables. Cela peut être considéré à l’origine comme une caractéristique, mais le premier problème devient beaucoup plus important à mesure que le nombre de variables augmente (le cas échéant).

4
Marc

Beaucoup de gens, y compris moi-même, ont besoin d'une fonction xor qui se comporte comme un circuit xor à n entrées, où n est variable. (Voir https://en.wikipedia.org/wiki/XOR_gate ). La fonction simple suivante implémente ceci.

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)

Exemple d'E/S suit:

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
4
Phillip M. Feldman

Xor est ^ en Python. Il retourne:

  • Un bitwise xor pour ints
  • Xor logique pour bools
  • Une union exclusive pour les décors
  • Résultats définis par l'utilisateur pour les classes qui implémentent __xor__.
  • TypeError pour les types non définis, tels que les chaînes ou les dictionnaires.

De toute façon, si vous avez l’intention de les utiliser sur des chaînes, les insérer dans bool rend votre opération sans ambiguïté (vous pouvez également dire set(str1) ^ set(str2)).

4
Arthur Havlicek

Python a un opérateur OU exclusif au niveau du bit, c’est ^:

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

Vous pouvez l'utiliser en convertissant les entrées en booléens avant d'appliquer xor (^):

bool(a) ^ bool(b)

(Edité - merci Arel)

3
Tomer Gal

XOR est implémenté dans operator.xor.

3
lbolla

Pour obtenir le xor logique de deux variables ou plus en Python:

  1. Convertir les entrées en booléens
  2. Utiliser l'opérateur xor au niveau du bit (^ ou operator.xor)

Par exemple,

bool(a) ^ bool(b)

Lorsque vous convertissez les entrées en valeurs booléennes, au niveau du bit xor devient logique xor.

Notez que la réponse acceptée est fausse:!= n'est pas identique à xor dans Python en raison de la subtilité de l'enchaînement d'opérateurs .

Par exemple, le xor des trois valeurs ci-dessous est incorrect lorsque vous utilisez !=:

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

(P.S. j'ai essayé de modifier la réponse acceptée pour inclure cet avertissement, mais ma modification a été rejetée.)

2
Arel