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())TrueEt 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 = 1else: 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 ! :)