Python prend-il en charge les courts-circuits?

Réponses:

192

Comportement-circuit court dans l' opérateur and, or:

Définissons d'abord une fonction utile pour déterminer si quelque chose est exécuté ou non. Une fonction simple qui accepte un argument, imprime un message et renvoie l'entrée, inchangée.

>>> def fun(i):
...     print "executed"
...     return i
... 

On peut observer le comportement de court-circuit de Python de and, oropérateurs dans l'exemple suivant:

>>> fun(1)
executed
1
>>> 1 or fun(1)    # due to short-circuiting  "executed" not printed
1
>>> 1 and fun(1)   # fun(1) called and "executed" printed 
executed
1
>>> 0 and fun(1)   # due to short-circuiting  "executed" not printed 
0

Remarque: L'interpréteur considère que les valeurs suivantes signifient false:

        False    None    0    ""    ()    []     {}

Comportement en court-circuit en fonction: any(), all():

Les fonctions any()et Python all()prennent également en charge les courts-circuits. Comme indiqué dans les documents; ils évaluent chaque élément d'une séquence dans l'ordre, jusqu'à trouver un résultat permettant une sortie précoce de l'évaluation. Considérez les exemples ci-dessous pour comprendre les deux.

La fonction any()vérifie si un élément est vrai. Il cesse de s'exécuter dès qu'un True est rencontré et renvoie True.

>>> any(fun(i) for i in [1, 2, 3, 4])   # bool(1) = True
executed
True
>>> any(fun(i) for i in [0, 2, 3, 4])   
executed                               # bool(0) = False
executed                               # bool(2) = True
True
>>> any(fun(i) for i in [0, 0, 3, 4])
executed
executed
executed
True

La fonction all()vérifie que tous les éléments sont vrais et arrête de s'exécuter dès qu'un faux est rencontré:

>>> all(fun(i) for i in [0, 0, 3, 4])
executed
False
>>> all(fun(i) for i in [1, 0, 3, 4])
executed
executed
False

Comportement de court-circuit dans la comparaison en chaîne:

De plus, en Python

Les comparaisons peuvent être enchaînées arbitrairement ; par exemple, x < y <= zest équivalent à x < y and y <= z, sauf qu'il yn'est évalué qu'une seule fois (mais dans les deux cas zn'est pas évalué du tout lorsqu'il x < ys'avère faux).

>>> 5 > 6 > fun(3)    # same as:  5 > 6 and 6 > fun(3)
False                 # 5 > 6 is False so fun() not called and "executed" NOT printed
>>> 5 < 6 > fun(3)    # 5 < 6 is True 
executed              # fun(3) called and "executed" printed
True
>>> 4 <= 6 > fun(7)   # 4 <= 6 is True  
executed              # fun(3) called and "executed" printed
False
>>> 5 < fun(6) < 3    # only prints "executed" once
executed
False
>>> 5 < fun(6) and fun(6) < 3 # prints "executed" twice, because the second part executes it again
executed
executed
False

Edit:
Un autre point intéressant à noter : - Logiquement and, lesor opérateurs en Python retournent la valeur d' un opérande au lieu d'un booléen ( Trueou False). Par exemple:

L'opération x and ydonne le résultatif x is false, then x, else y

Contrairement à d'autres langages, par exemple &&, les ||opérateurs en C qui renvoient 0 ou 1.

Exemples:

>>> 3 and 5    # Second operand evaluated and returned 
5                   
>>> 3  and ()
()
>>> () and 5   # Second operand NOT evaluated as first operand () is  false
()             # so first operand returned 

De même, l' oropérateur retourne la valeur la plus à gauche pour laquelle bool(value)== Truesinon la valeur la plus fausse à droite (selon le comportement de court-circuit), exemples:

>>> 2 or 5    # left most operand bool(2) == True
2    
>>> 0 or 5    # bool(0) == False and bool(5) == True
5
>>> 0 or ()
()

Alors, comment est-ce utile? Un exemple d'utilisation donné dans Practical Python par Magnus Lie Hetland:
Disons qu'un utilisateur est censé entrer son nom, mais peut choisir de ne rien entrer, auquel cas vous souhaitez utiliser la valeur par défaut '<unknown>'. Vous pouvez utiliser une instruction if, mais vous pouvez également énoncer les choses très succinctement:

In [171]: name = raw_input('Enter Name: ') or '<Unkown>'
Enter Name: 

In [172]: name
Out[172]: '<Unkown>'

En d'autres termes, si la valeur de retour de raw_input est vraie (pas une chaîne vide), elle est affectée à name (rien ne change); sinon, la valeur par défaut '<unknown>'est affectée à name.

Grijesh Chauhan
la source
1
Petit problème: la liste explicite des valeurs de falsification est légèrement trompeuse. Tout type peut avoir une ou plusieurs valeurs fausses. Par convention, tous les types numériques avec valeur 0sont falsy (il est donc pas seulement 0, il est 0.0, 0j, decimal.Decimal(0), fractions.Fraction(0), etc.), comme toutes les collections de longueur 0(donc au - dessus de ce que vous avez énumérés, b''[PY3], u''[AP2] et set()/ frozenset()sont tous les prédéfinis qui sont évalués comme étant faux), mais les types définis par l'utilisateur / tiers peuvent définir les leurs (avec __bool__[Py3] / __nonzero__[Py2] directement ou indirectement en définissant __len__).
ShadowRanger
@ShadowRanger ici votre commentaire complètera ma réponse. merci d'avoir ajouté cette note.
Grijesh Chauhan
De plus, python double-évalue les conditions en court-circuit, si elles sont utilisées plus tard comme booléens ... sauf si elles sont dans une instruction if, qui est privilégiée: gist.github.com/earonesty/08e9cbe083a5e0583feb8a34cc538010
Erik Aronesty
48

Oui. Essayez ce qui suit dans votre interpréteur python:

et

>>>False and 3/0
False
>>>True and 3/0
ZeroDivisionError: integer division or modulo by zero

ou

>>>True or 3/0
True
>>>False or 3/0
ZeroDivisionError: integer division or modulo by zero
Caprooja
la source