#!⌨

La magie des opérations booléennes

J'étais persuadé qu'avec Python, les opérations booléennes x and y et x or y ne retournait que des booléens. Mais en fait, non ! Et je m'émerveille de leur fonctionnement réel ! :)

Et pourtant, j'avais déjà utilisé des syntaxes du genre x = y or 0 qui donne la valeur 0 à x si y est évalué à False, et y sinon. J'avais utilisé cette syntaxe sans savoir ce qu'il se passait derrière, et je trouvais cette syntaxe pratique. Hier, j'ai relu du code et j'ai trouvé quelque chose du genre x = y and 1 or 0. Et là, je me suis dit "Hein ?".

Voici donc l'explication : or et and évaluent la première variable et retournent l'une deux variable. x or y retourne x si x est évalué à True, et y sinon. x and y retourne y si x est évalué à True, et x sinon. Pas très clair ? Vous retrouverez le détail des explications dans la documentation officielle des opérations booléennes. Et voici quelques exemples :

>>> 1 and 'a'
'a'
>>> 0 and 'a'
0
>>> 1 or 'a'
1
>>> 0 or 'a'
'a'
>>> '' or 'a'
'a'
>>> [] or 'a'
'a'
>>> True and 'a'
'a'
>>> False and 'a'
False
>>> True or 'a'
True
>>> False or 'a'
'a'
>>>

L'évaluation d'une variable fonctionne comme la conversion en booléen : elle utilise la méthode spéciale __nonzero__ en Python 2 (doc de __nonzero__) et __bool__ en Python 3 (doc de __bool__). Si cette méthode n'est pas définie, elle regarde le retour de la méthode __len__. Et si aucune de ces deux méthodes n'est définie, alors c'est True.

Voici des exemples dans la console interactive Python 3 :

>>> class test_true:
...     def __bool__(self): # __nonzero__ en Python 2
...         return True
...
>>> bool(test_true())
True
>>> class test_false:
...     def __bool__(self): # __nonzero__ en Python 2
...         return False
...
>>> bool(test_false())
False
>>> class test_len_0:
...     def __len__(self):
...         return 0
...
>>> bool(test_len_0())
False
>>> class test_len_1:
...     def __len__(self):
...         return 1
...
>>> bool(test_len_1())
True
>>> class test_pass:
...     pass
...
>>> bool(test_pass())
True
Et la syntaxe x = y and 1 or 0, peut être décortiquée de la manière suivante :
  • si bool(y) est True alors y and 1 renvoie 1 et 1 or 0 renvoie 1.
  • si bool(y) est False, alors y and 1 renvoie y et y or 0 renvoie 0.

Et cette syntaxe correspond à l'algorithme suivant :

if y:
    x = 1
else:
    x = 0

Mais x = y and 1 or 0 est bien plus court et pratique.

C'est bête, mais à chaque que je regarde un peu les entrailles de Python, je m'émerveille un peu plus ! :)