XOR en Python : opérateur ^, table de vérité et exemples bitwise

XOR en Python : opérateur ^, table de vérité et exemples bitwise

XOR signifie “ou exclusif”. L’idée est simple : le résultat est vrai quand les deux valeurs sont différentes, et faux quand elles sont identiques.

En Python, l’opérateur XOR s’écrit ^. Sur des entiers, il compare les bits un par un. Sur des booléens, il permet de vérifier qu’une seule des deux conditions est vraie.

print(5 ^ 3)          # 6
print(True ^ False)   # True
print(True ^ True)    # False

Le piège classique est de croire que ^ sert à calculer une puissance. En Python, la puissance s’écrit avec **, pas avec ^.

print(2 ** 3)  # 8
print(2 ^ 3)   # 1

Cette différence explique beaucoup de résultats “bizarres” chez les débutants.

La réponse courte

Pour faire un XOR en Python, utilisez l’opérateur ^.

a = 5      # 0101 en binaire
b = 3      # 0011 en binaire

resultat = a ^ b

print(resultat)        # 6
print(f"{resultat:04b}")  # 0110

Le XOR compare chaque bit :

0101
0011
----
0110

Un bit vaut 1 dans le résultat seulement si les deux bits comparés sont différents.

La table de vérité du XOR

La table de vérité du XOR tient en quatre cas :

A B A XOR B
False False False
False True True
True False True
True True False

Autrement dit : XOR répond à la question “exactement une des deux conditions est-elle vraie ?”.

En Python :

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

print(xor_logique(False, False))  # False
print(xor_logique(False, True))   # True
print(xor_logique(True, False))   # True
print(xor_logique(True, True))    # False

Pour des booléens, a != b est souvent plus lisible que a ^ b, car il exprime directement l’idée de différence.

utilisateur_connecte = True
mode_invite = False

acces_possible = utilisateur_connecte != mode_invite

print(acces_possible)  # True

Comment Python calcule un XOR bit par bit

Sur des entiers, Python applique XOR à l’écriture binaire des nombres. Prenons 5 ^ 3.

a = 5
b = 3

print(f"{a:04b}")  # 0101
print(f"{b:04b}")  # 0011
print(f"{a ^ b:04b}")  # 0110

Lecture bit par bit :

Bit de a Bit de b Résultat
0 0 0
1 0 1
0 1 1
1 1 0

Le résultat binaire 0110 vaut 6 en décimal.

Vous pouvez afficher les étapes dans une fonction :

def afficher_xor(a, b, largeur=8):
    resultat = a ^ b
    print(f"a        = {a:>{largeur}d}  {a:0{largeur}b}")
    print(f"b        = {b:>{largeur}d}  {b:0{largeur}b}")
    print(f"a ^ b    = {resultat:>{largeur}d}  {resultat:0{largeur}b}")
    return resultat


afficher_xor(5, 3, largeur=4)

Résultat :

a        =    5  0101
b        =    3  0011
a ^ b    =    6  0110

Cette visualisation est très utile pour comprendre les masques binaires.

XOR, OR et AND : ne pas confondre

Les trois opérateurs bitwise les plus fréquents sont &, | et ^.

Opérateur Nom Idée simple
& AND bitwise garde 1 si les deux bits valent 1
| OR bitwise garde 1 si au moins un bit vaut 1
^ XOR bitwise garde 1 si les deux bits sont différents

Exemple :

a = 0b1100
b = 0b1010

print(f"{a & b:04b}")  # 1000
print(f"{a | b:04b}")  # 1110
print(f"{a ^ b:04b}")  # 0110

Le XOR n’est pas un simple “ou”. Avec OR, deux valeurs vraies donnent vrai. Avec XOR, deux valeurs vraies donnent faux.

print(True or True)  # True
print(True ^ True)   # False

C’est précisément ce qui rend XOR utile : il détecte une différence.

Utiliser XOR avec des booléens

Python accepte ^ entre deux booléens :

print(False ^ False)  # False
print(False ^ True)   # True
print(True ^ False)   # True
print(True ^ True)    # False

Mais dans du code applicatif, préférez souvent :

condition_a != condition_b

Exemple :

mot_de_passe_fourni = True
connexion_oauth = False

if mot_de_passe_fourni != connexion_oauth:
    print("Une seule méthode de connexion est utilisée.")
else:
    print("Configuration ambiguë.")

Cette forme est plus claire pour un lecteur qui ne pense pas immédiatement aux opérations bitwise.

Résoudre une équation XOR simple

XOR possède trois propriétés très pratiques :

x ^ x = 0
x ^ 0 = x
x ^ y = y ^ x

Ces propriétés permettent de retrouver une valeur inconnue.

Si :

a ^ b = c

alors :

a = c ^ b
b = c ^ a

Exemple en Python :

a = 42
b = 15

c = a ^ b

retrouve_a = c ^ b
retrouve_b = c ^ a

print(c)          # 37
print(retrouve_a) # 42
print(retrouve_b) # 15

Pourquoi cela fonctionne ?

c ^ b = (a ^ b) ^ b
      = a ^ (b ^ b)
      = a ^ 0
      = a

Cette logique revient souvent dans les exercices d’algorithmique.

Trouver l’élément unique dans une liste

Un exemple classique : dans une liste, tous les nombres apparaissent deux fois sauf un. XOR permet de retrouver ce nombre en une seule passe.

from functools import reduce
from operator import xor

valeurs = [9, 12, 9, 4, 12]

unique = reduce(xor, valeurs)

print(unique)  # 4

Explication : chaque paire s’annule.

9 ^ 9 = 0
12 ^ 12 = 0
0 ^ 0 ^ 4 = 4

Version sans reduce, plus lisible pour débuter :

valeurs = [9, 12, 9, 4, 12]
resultat = 0

for valeur in valeurs:
    resultat ^= valeur

print(resultat)  # 4

Ce code est en O(n) en temps et en O(1) en mémoire. Il est donc très efficace pour ce cas précis.

Attention : cette méthode suppose que tous les autres nombres apparaissent exactement deux fois. Si les doublons ne respectent pas cette règle, le résultat ne correspond plus à “l’élément unique”.

Basculer un bit avec XOR

XOR est très pratique pour inverser certains bits. Si un bit est à 0, XOR avec 1 le met à 1. S’il est à 1, XOR avec 1 le remet à 0.

valeur = 0b1010
masque = 0b0010

resultat = valeur ^ masque

print(f"{valeur:04b}")    # 1010
print(f"{masque:04b}")    # 0010
print(f"{resultat:04b}")  # 1000

Le deuxième bit en partant de la droite a été inversé.

Pour basculer plusieurs bits, mettez plusieurs 1 dans le masque :

valeur = 0b1010
masque = 0b0101

print(f"{valeur ^ masque:04b}")  # 1111

Ce type de manipulation apparaît dans les flags binaires, les protocoles, certaines optimisations, les formats de fichiers ou les exercices de bas niveau.

XOR avec des ensembles Python

L’opérateur ^ ne sert pas seulement aux entiers. Python l’utilise aussi pour la différence symétrique entre ensembles.

a = {"python", "numpy", "pandas"}
b = {"python", "django", "pandas"}

print(a ^ b)

Résultat possible :

{'numpy', 'django'}

Ici, a ^ b garde les éléments présents dans un seul des deux ensembles, mais pas dans les deux. L’idée est la même que XOR : conserver ce qui diffère.

Si vous voulez une version plus explicite :

print(a.symmetric_difference(b))

Pour du code très lisible, symmetric_difference() peut être préférable. Pour du code concis, ^ est courant.

XOR et cryptographie : utile pour apprendre, pas pour sécuriser

XOR apparaît souvent dans les cours de cryptographie, car il permet de combiner une donnée avec une clé de manière réversible.

Exemple pédagogique avec des octets :

message = b"python"
cle = 0b01010101

chiffre = bytes(octet ^ cle for octet in message)
dechiffre = bytes(octet ^ cle for octet in chiffre)

print(chiffre)
print(dechiffre)  # b'python'

Le même XOR appliqué deux fois avec la même clé revient au message initial.

Mais ce n’est pas une méthode de sécurité suffisante. Une clé courte, répétée ou prévisible se casse facilement. Pour un vrai besoin de chiffrement, utilisez une bibliothèque de cryptographie reconnue et un protocole adapté.

XOR reste en revanche excellent pour comprendre les opérations binaires, les masques et les propriétés réversibles.

Les erreurs fréquentes avec XOR en Python

Utiliser ^ pour une puissance

En Python, ^ ne calcule pas une puissance.

print(2 ^ 8)   # 10
print(2 ** 8)  # 256

Si vous venez d’un langage, d’une calculatrice ou d’un contexte mathématique où ^ signifie puissance, ce piège est très courant.

Mélanger logique booléenne et bitwise

Pour exprimer “au moins une condition”, utilisez or.

if age_majeur or autorisation_parentale:
    print("Accès possible")

Pour exprimer “exactement une condition”, utilisez plutôt != entre booléens :

if age_majeur != autorisation_parentale:
    print("Une seule condition est vraie")

L’opérateur ^ fonctionne, mais il est moins parlant pour beaucoup de lecteurs.

Appliquer XOR à des nombres décimaux

XOR fonctionne sur des entiers, pas sur des flottants.

print(5 ^ 3)      # OK
print(5.0 ^ 3.0)  # TypeError

Si vous manipulez des nombres décimaux, vous n’êtes probablement pas dans un usage bitwise.

Oublier les parenthèses dans les expressions

Les opérateurs bitwise ont leur propre priorité. Pour éviter les ambiguïtés, mettez des parenthèses dans les expressions non triviales.

resultat = (a & masque) ^ cle

C’est plus lisible que :

resultat = a & masque ^ cle

Même quand Python interprète correctement l’expression, le lecteur gagne du temps.

Manipuler des nombres négatifs sans masque

Avec les entiers négatifs, l’affichage binaire peut surprendre, car Python modélise les opérations bitwise comme si les entiers avaient une extension de signe.

print(-1 ^ 0b1111)  # -16

Si vous voulez raisonner sur 8 bits, appliquez un masque :

resultat = (-1 ^ 0b1111) & 0xff

print(resultat)        # 240
print(f"{resultat:08b}")  # 11110000

Le masque force la lecture sur la largeur choisie.

operator.xor : la version fonction

Python expose aussi operator.xor, qui correspond à l’opérateur ^.

from operator import xor

print(xor(5, 3))  # 6

Cette fonction est surtout utile avec des outils comme reduce, map ou quand vous voulez passer l’opération en paramètre.

from functools import reduce
from operator import xor

valeurs = [1, 2, 3, 2, 1]

print(reduce(xor, valeurs))  # 3

Dans du code simple, a ^ b reste plus direct.

Petit mémo des cas d’usage

Besoin Forme Python recommandée
XOR bitwise entre entiers a ^ b
XOR logique entre booléens a != b
Fonction XOR réutilisable operator.xor(a, b)
Trouver un élément unique avec des paires boucle avec resultat ^= valeur
Basculer un bit valeur ^ masque
Différence symétrique entre ensembles a ^ b ou a.symmetric_difference(b)
Puissance a ** b, jamais a ^ b

Exercices rapides

Exercice 1

Prédisez le résultat :

print(7 ^ 3)

Correction :

print(f"{7:04b}")      # 0111
print(f"{3:04b}")      # 0011
print(f"{7 ^ 3:04b}")  # 0100
print(7 ^ 3)           # 4

Exercice 2

Complétez la valeur manquante :

a = 23
b = ...  # à compléter
c = 31

# a ^ b == c

Correction :

a = 23
c = 31

b = a ^ c

print(b)
print(a ^ b == c)  # True

Exercice 3

Retrouvez l’élément qui n’apparaît qu’une seule fois :

valeurs = [14, 8, 14, 3, 8, 21, 3]

Correction :

resultat = 0

for valeur in valeurs:
    resultat ^= valeur

print(resultat)  # 21

Pour aller plus loin

XOR est une petite opération, mais elle force à bien comprendre la différence entre logique booléenne, calcul binaire et opérateurs Python. C’est aussi un bon pont vers des sujets plus larges : masques, flags, chiffrement pédagogique, ensembles, complexité et algorithmes.

Pour continuer dans le même esprit :

La règle à retenir est simple : ^ sert au XOR, ** sert à la puissance, et a != b reste souvent la forme la plus lisible pour un XOR logique entre deux booléens.

Références