Sam & Max » condition http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Quelques astuces à propos de and et or 19 http://sametmax.com/quelques-astuces-a-propos-de-and-et-or/ http://sametmax.com/quelques-astuces-a-propos-de-and-et-or/#comments Tue, 30 Jun 2015 13:46:29 +0000 http://sametmax.com/?p=16421 and et or sont écrits && et ||. Ces symboles existent en Python, mais ils sont là pour appliquer des opérations binaires. Ce n'est néanmoins pas la seule bizarrerie de Python dans le domaine.]]> Dans beaucoup de langages populaires, and et or sont écrits && et ||. Ces symboles existent en Python, mais ils sont là pour appliquer des opérations binaires :

>>> bin(0b010 & 0b111)
'0b10'
 
>>> bin(0b010 | 0b111)
'0b111'

Ce n’est néanmoins pas la seule bizarrerie de Python dans le domaine.

Shortcuts

Les opérateurs and et or court-circuitent les conditions dès que possible, c’est à dire qu’ils retournent la valeur au plus tôt, même si ça signifie ne pas exécuter tout le code.

Par exemple, prenons deux fonctions:

def vrai():
    print('Yeah !')
    return True
 
def faux():
    print('Errrr...')
    return False

Si je fais un or dessus, ça va me retourner True, et afficher deux messages :

>>> faux() or vrai()
Errrr...
Yeah !
True

Mais si j’INVERSE les deux fonctions, alors je n’aurais qu’un seul message qui va s’afficher :

>>> vrai() or faux()
Yeah !
True

La raison est que or sait qu’il peut retourner True dès qu’il obtient au moins une valeur True. vrai() retourne True, donc or sait que tout la condition sera forcément vraie, et il n’exécute pas le code du reste de la condition. Ainsi, faux() n’est jamais appelée.

and fait pareil :

>>> vrai() and faux()
Yeah !
Errrr...
False

Et à l’envers :

>>> faux() and vrai()
Errrr...
False

Car dans le second cas, and sait qu’il doit avoir toutes les valeurs à True pour renvoyer True. Comme il reçoit False dès le premier test, il ne va pas plus loin, et vrai() n’est jamais appelée.

Le but de cette fonctionnalité est d’autoriser le développeur à mettre les fonctions qui sont les plus gourmandes en ressource tout à droite de la condition, ainsi elle ne seront pas toujours appelées, ce qui améliore les perfs.

Si vous avez besoin que les fonctions soient toujours appelées car elles ont des effets de bord (c’est mal, boooouh !), il suffit de mettre leurs résultats dans des variables :

>>> a = vrai()
Yeah !
>>> b = faux()
Errrr...
>>> b and a
False

Pas de bool

La plupart des opérateurs utilisés pour faire des tests retournent des booléans :

>>> 1 > 2
False
 
>>> "a" in "chat"
True

Mais and et or ne retournent pas des booléans. Dès qu’ils sont certains du résultats de la condition, ils retournent la valeurs qu’ils ont sous la main.

Cela est du au fait qu’en Python, tout a une valeur True ou False dans un contexte booléen. Pour faire simple, n’importe quel objet mis dans une condition vaut soit True, soit False.

Par exemple, une liste vide vaut False dans une condition, une liste non vide vaut True :

>>> couleurs = []
>>> if couleurs:
   ...:     print("J'ai une couleur !")
>>> couleurs.append('rouge')
>>> if couleurs:
    print("J'ai une couleur !")
J'ai une couleur !

On peut le vérifier facilement :

>>> bool([])
    False
>>> bool(['rouge'])
    True

Il est facile de se souvenir de ce qui est faux ou vrai en Python. False, None, 0 et tout ce qui est vide est faux :

>>> for x in (False, None, 0, "", [], set(), {}, ()):
   ...:     print(type(x), bool(x))
   ...:     
<class 'bool'>, False
<class 'NoneType'>, False
<class 'int'>, False
<class 'str'>, False
<class 'list'>, False
<class 'set'>, False
<class 'dict'>, False
<class 'tuple'>, False

Tout le reste est vrai :

>>> for x in (Ellipsis, True, 432, "foo", ["bar"],  set("ba"), {"pa": "pa"}, 
              ("doh",), lambda : None, len):
    print(type(x), bool(x))
<class 'ellipsis'> True
<class 'bool'> True
<class 'int'> True
<class 'str'> True
<class 'list'> True
<class 'set'> True
<class 'dict'> True
<class 'tuple'> True
<class 'function'> True
<class 'builtin_function_or_method'> True

Du coup, and et or vont vérifier la valeur de chaque objet de la condition, et retourner le premier à partir duquel ils sont certains du résultat de la condition entière.

Par exemple, si je fais :

>>> True and True and False and False
    False

and n’est certain que la condition est fausse qu’au moment où on attend le premier False. C’est donc ce False qu’il retourne.

Cela est beaucoup plus clair quand on le fait avec des objets plus complexes :

>>> "a" and 1 and [] and {}
    []

Puisque :

>>> bool('a')
    True
>>> bool(1)
    True
>>> bool([])
    False
>>> bool({})
    False

and n’est certain du résultat de la condition qu’en arrivant sur [], qu’il retourne.

Si tous les éléments sont vrais, il va donc prendre le dernier :

>>> "a" and 1 and True and [1, 2, 3]
    [1, 2, 3]

C’est la même chose pour or :

>>> "" or None or False or 0
    0

Là, or ne peut pas savoir si la condition est fausse avant d’arriver au tout dernier élément, qu’il retourne.

Mais si je glisse un truc vrai dans le lot :

>>> "" or {1: 2} or False or 0
    {1: 2}

Comme il n’a besoin que d’un élément vrai pour que toute la condition soit vraie, dès qu’il en rencontre un, il le retourne.

Il n’y a pas de XOR

Le “ou” exclusif, opération qui retourne vrai seulement si un élément est vrai mais pas l’autre, n’existe pas sous la forme d’un opérateur en Python. Évidement on peut l’émuler manuellement :

def xor(a, b):
    return (a and not b) or (not a and b)

Mais une astuce de sioux permet un résultat plus court avec une syntaxe un poil plus proche des langages qui possèdent cet opérateur :

bool(a) ^ bool(b)

Exemple :

>>> bool(['pomme']) ^ bool([])
    True
>>> bool(['pomme']) ^ bool(['banane'])
    False

^ est en effet l’opérateur XOR pour les opérations binaires. La partie marrante, c’est qu’en Python :

>>> True == 1
    True
>>> False == 0
    True

Et comme :

>>> 1 ^ 1
    0
>>> 1 ^ 0
    1

Alors:

>>> True ^ True
    False
>>> True ^ False
    True

On obtient le résultat voulu.

Oui, c’est un peu tordu, je vous l’accorde.

]]>
http://sametmax.com/quelques-astuces-a-propos-de-and-et-or/feed/ 19