La magie des booléens en Python
vendredi 4 octobre 2013 à 12:11Dans un langage fortement typé comme Python, on ne peut pas additionner des choux et des carottes, comme disait madame Germaine, ma prof de mate.
Par exemple on ne peut pas faire ça :
>>> 1 + "1" Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'str'
Nonnnnnnnn !
Ni ça :
>>> [1] + (1,) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate list (not "tuple") to list
Pas biennnnnnnnnnnnnn !
Et pas ça :
>>> True + 1 2
OUATE ZE PHOQUE ?
Les booléens sont des entiers déguisés
Pendant longtemps, Python n’a pas eu de type bool, et on utilisait, comme en C, 0 pour faux, et 1 pour vrai. Ça marchait pas mal, mais dans un souci de rentre le langage plus propre, on a voulu créer une type spécialisé, et ainsi :
>>> type(True) <class 'bool'> >>> type(1) <class 'int'>
Mais il faut avouer que pouvoir interchanger True pour 1 et False pour 0 et vice versouille, c’était achement pratique dans une discipline ou la manipulation des chiffres à des fins logiques est un peu la base du métier. Du coup il a été décidé que les booléen seraient des travs, juste une surcouche au dessus des entiers, et qu’on pourrait les utiliser en lieu et place de ceux-ci. Une exception étonnante vu la rigueur du langage sur la gestion des types.
Du coup :
>>> True == 1 True >>> False == 0 True >>> True + 1 2 >>> False - 1 -1
Par contre :
>>> 1 is 1 True >>> True is True True >>> True is 1 False
Et qui dit exception, dit hack
Donc voici quelques moyens de détourner l’utilisation des booléens pour écrire du Perlthon…
On peut utiliser un bool pour de l’indexing ou du slicing :
>>> ('Valeur 1', 'Valeur 2')[True] 'Valeur 2' >>> list(range(10)[True:False-1:2]) [1, 3, 5, 7]
On peut utiliser un bool pour multiplier, et l’opération multiplier marche sur les strings et les listes…
>>> "J'aime les chat" + "s" * est_pluriel "J'aime les chat" >>> est_pluriel = True >>> "J'aime les chat" + "s" * est_pluriel "J'aime les chats" >>> def process(lst, cancel=False): ... lst = list(lst) * (not cancel) ... for i in lst: ... print(i) ... >>> process(range(3)) 0 1 2 >>> process(range(3), True)
Ou même pire, saviez-vous que la déclaration d’héritage et les clauses d’exception pouvaient inclure une expression ? En rajoutant de l’unpacking, on obtient un truc bien marrant :
>>> class Parent: ... foo = "bar" ... >>> inherit = False >>> class Enfant(*[Parent] * inherit): ... pass ... >>> Enfant().foo Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Enfant' object has no attribute 'foo' >>> inherit = True >>> class Enfant(*[Parent] * inherit): ... pass ... >>> Enfant().foo 'bar' >>> catch = True >>> try: ... 42 / 0 ... except (ZeroDivisionError,) * catch: ... print("Nope") Nope >>> catch = False >>> try: ... 42 / 0 ... except (ZeroDivisionError,) * catch: ... print("Nope") Traceback (most recent call last): File "<ipython-input-12-c01f7e27284d>", line 2, in <module> 42 / 0 ZeroDivisionError: division by zero
Évidelement ça marche avec n’importe quels entiers, mais où serait le fun ?
Et on peut utiliser un bool pour incrémenter une valeur.
>>> increment = True >>> value = 0 >>> for x in range(10): ... value += increment ... >>> value 10 >>> increment = False >>> for x in range(10): ... value += increment ... >>> value 10
Voilà de quoi bien vous marrer au boulot lors de votre prochaine code review !