4.6. Particularités de and et or

En Python, and et or appliquent la logique booléenne comme vous pourriez l’attendre, mais ils ne retournent pas de valeurs booléennes, ils retournent une des valeurs comparées.

Exemple 4.15. Présentation de and

>>> 'a' and 'b'         1
'b'
>>> '' and 'b'          2
''
>>> 'a' and 'b' and 'c' 3
'c'
1 Lorsqu’on utilise and les valeurs sont évaluées dans un contexte booléen de gauche à droite. 0, '', [], (), {} et None valent faux dans ce contexte, tout le reste vaut vrai.[1]Si toutes les valeurs valent vrai dans un contexte booléen, and retourne la dernière valeur. Ici and évalue 'a', qui vaut vrai, puis 'b', qui vaut vrai et retourne 'b'.
2 Si une des valeurs vaut faux and retourne la première valeur fausse. Ici '' est la première valeur fausse.
3 Toutes les valeurs sont vrai, donc and retourne la dernière valeur, 'c'.

Exemple 4.16. Présentation de or

>>> 'a' or 'b'          1
'a'
>>> '' or 'b'           2
'b'
>>> '' or [] or {}      3
{}
>>> def sidefx():
...     print "in sidefx()"
...     return 1
>>> 'a' or sidefx()     4
'a'
1 Lorsqu’on utilise or, les valeurs sont évaluées dans un contexte booléen de gauche à droite, comme pour and. Si une des valeurs vaut vrai, or la retourne immédiatement. Dans ce cas 'a' est la première valeur vraie.
2 or évalue '', qui vaut faux, puis 'b', qui vaut vrai et retourne 'b'.
3 Si toutes les valeurs valent faux, or retourne la dernière valeur. or évalue '', qui vaut faux, puis [], qui vaut faux, puis {}, qui vaut faux et retourne {}.
4 Notez que or continue l’évaluation seulement jusqu’à ce qu’il trouve une valeur vraie, le reste est ignoré. C’est important si certaines valeurs peuvent avoir un effet de bord. Ici, la fonction sidefx n’est jamais appelée, car or évalue 'a', qui vaut vrai et retourne 'a' immédiatement.

Si vous êtes un programmeur C, vous êtes certainement familier de l’expression ternaire bool ? a : b, qui s’évalue à a si bool vaut vrai et à b dans le cas contraire. Le fonctionnement de and et or en Python vous permet d’accomplir la même chose.

4.6.1. Utilisation de l'astuce and-or

Exemple 4.17. Présentation de l’astuce and-or

>>> a = "first"
>>> b = "second"
>>> 1 and a or b 1
'first'
>>> 0 and a or b 2
'second'
1 Cette syntaxe ressemble à celle de l’expression ternaire bool ? a : b de C. L’expression est évaluée de gauche à droite, donc le and est évalué en premier. 1 and 'first' s’évalue à 'first', puis 'first' or 'second' s’évalue à 'first'.
2 0 and 'first' s’évalue à 0, puis 0 or 'second' s’évalue à 'second'.

Cependant, puisque cette expression en Python est simplement de la logique booléenne et non un dispositif spécial du langage, il y a une différence très, très importante entre l’astuce and-or en Python et la syntaxe bool ? a : b en C. Si a vaut faux, l’expression ne fonctionnera pas comme vous vous y attendez. (Vous devinez que cela m’a déjà joué des tours. Et plus d’une fois !)

Exemple 4.18. Quand l’astuce and-or échoue

>>> a = ""
>>> b = "second"
>>> 1 and a or b         1
'second'
1 Puisque a est une chaîne vide, ce que Python évalue à faux dans un contexte booléen, 1 and '' s’évalue à '', puis '' or 'second' s’évalue à 'second'. Zut ! Ce n’est pas ce que nous voulions.

L’astuce and-or, bool and a or b, ne fonctionne pas comme l’expression ternaire de C bool ? a : b quand a s’évalue à faux dans un contexte booléen.

La véritable astuce cachée derrière l’astuce and-or, c’est de s’assurer que a ne vaut jamais faux. Une manière habituelle de le faire est de changer a en [a] et b en [b] et de prendre le premier élément de la liste retournée, qui sera soit a soit b.

Exemple 4.19. L’astuce and-or en toute sécurité

>>> a = ""
>>> b = "second"
>>> (1 and [a] or [b])[0] 1
''
1 Puisque [a] est une liste non-vide, il ne vaut jamais faux. Même si a est 0 ou '' ou une autre valeur fausse, la liste [a] vaut vrai puisqu’elle a un élément.

On peut penser que cette astuce apporte plus de complication que d’avantages. Après tout, on peut obtenir le même résultat avec une instruction if, alors pourquoi s’embarasser de tout ces problèmes ? Mais dans de nombreux cas, le choix se fait entre deux valeurs constantes et donc vous pouvez utiliser cette syntaxe plus simple sans vous inquiéter puisque vous savez que a vaudra toujours vrai. Et même si vous devez utiliser la version sûre plus complexe, il y a parfois de bonnes raisons de le faire, il y a des cas en Python où les instructions if ne sont pas autorisées, comme dans les fonctions lambda.

Pour en savoir plus sur l'astuce and-or

Footnotes

[1] Presque tout, en fait. Par défaut, les instances de classes valent vrai dans un contexte booléen mais vous pouvez définir des méthodes spéciales de votre classe pour faire qu’une instance vale faux. Vous apprendrez tout sur les classes et les méthodes spéciales au Chapitre 5.