Comment fonctionne la solution de contournement de l'opérateur conditionnel Python?

D'après ce que j'ai lu, j'ai trouvé qu'un opérateur ternaire intégré n'existe pas (je serai heureux d'en savoir plus à ce sujet.).

J'ai trouvé le code suivant comme substitut:

def val():
    var = float(raw_input("Age:"))
    status = ("Working","Retired")[var>65]
    print "You should be:",status

Je ne comprenais pas comment ce code fonctionne; quelqu'un peut-il m'expliquer comment le code fonctionne? Je suis également intéressé de savoir pourquoi l'opérateur ternaire n'existe pas; toutes les références ou liens à ce sujet seront utiles.

Je cours Python 2.6.4 sur Windows Vista.

21
demandé sur Mechanical snail 2009-12-22 18:22:26

12 réponses

Python a une construction qui est un peu comme l'opérateur ternaire en C, et al. Cela fonctionne comme ceci:

my_var = "Retired" if age > 65 else "Working"

Et est équivalent à ce code C:

my_var = age > 65 ? "Retired" : "Working";

Quant à la façon dont le code que vous avez posté fonctionne, passons à travers:

("Working","Retired")

Crée un 2-tuple (une liste immuable) avec l'élément "Working" à l'index 0, et "Retired" à l'index 1.

var>65

Renvoie True si var est supérieur à 65, False sinon. Lorsqu'il est appliqué à un indice, il est converti en 1 (True) ou 0 (Faux). Ainsi, cette valeur booléenne fournit un index dans le tuple créé sur la même ligne.

Pourquoi Python n'a-t-il pas toujours eu un opérateur ternaire? La réponse simple est que Guido van Rossum, l'auteur de Python, n'a pas aimé/ne voulait pas, croyant apparemment que c'était une construction inutile qui pourrait conduire à un code confus (et quiconque a vu des opérateurs ternaires massivement imbriqués en C peut probablement être d'accord). Mais pour Python 2.5, il a cédé et ajouté la grammaire vue ci-dessus.

51
répondu mipadi 2012-03-21 20:46:02

Python (2.5 et supérieur) a en effet une syntaxe pour ce que vous recherchez:

x = foo if condition else bar

Si condition est Vrai, x sera mis à foo, sinon il sera mis à bar.

Exemples:

>>> age = 68
>>> x = 'Retired' if age > 65 else 'Working'
>>> x
'Retired'
>>> age = 35
>>> y = 'Retired' if age > 65 else 'Working'
>>> y
'Working'
9
répondu TM. 2009-12-22 15:35:47

Parce que True convertit en 1 et False convertit en 0 donc si var = 70

("Working","Retired")[var>65]

Devient

("Working", "Retired")[1]

Un joli petit raccourci ... mais je trouve que cela peut être un peu déroutant avec autre chose qu'une condition simple, donc j'irais avec la suggestion de TM

"Retired" if var > 65 else "Working"
8
répondu mozillalives 2009-12-22 15:31:01

Indexation dans une liste

L'utilisation de

[expression_when_false, expression_when_true][condition] # or
(expression_when_false, expression_when_true)[condition]

Profite du fait que dans Python True equals (mais n'est pas!) 1 et False est égal à (mais n'est pas!) 0. L'expression ci-dessus construit une liste de deux éléments et utilise le résultat de condition pour indexer dans la liste et renvoyer une seule expression. L'inconvénient de cette méthode est que les deux expressions sont évaluées.

Et-ou raccourcis

Depuis la création de Python, il y avait une forme de ceci fonctionnement:

condition and expression_when_true or expression_when_false

Cela prend un raccourci et évalue une seule expression, mais a un inconvénient sujet aux bugs: le expression_when_true ne doit pas évaluer à une valeur non vraie, sinon le résultat est expression_when_false. and et or sont "court-circuit" en Python, et les règles suivantes s'appliquent:

a and b #→ a if a is false, else b
a or b  #→ a if a is true, else b

Si condition est faux, alors expression_when_true n'est jamais évaluée et le résultat est expression_when_false. Otoh, que, si condition est vraie, alors le résultat est le résultat de (expression_when_true ou expression_when_false); consulter le tableau ci-dessus.

Opérateur conditionnel Ternaire

Bien sûr, depuis Python 2.5, il existe un opérateur conditionnel ternaire:

expression_when_true if condition else expression_when_false

L'ordre étrange (si vous êtes habitué à L'opérateur conditionnel ternaire de type C) des opérandes est attribué à beaucoup de choses ; le l'intention générale est que condition devrait être vrai la plupart du temps, de sorte que la sortie la plus commune vient en premier et est la plus visible.

7
répondu tzot 2018-03-12 09:34:35

Expressions booléennes de court-circuit

Il y a aussi une option pour court-circuiter les opérations logiques:

>>> (2+2 == 4) and "Yes" or "No"
'Yes'
>>> (2+2 == 5) and "Yes" or "No"
'No'

Dans votre exemple:

>>> (int(raw_input("Age: ")) > 65) and "Retired" or "Working"
Age: 20
'Working'
>>> (int(raw_input("Age: ")) > 65) and "Retired" or "Working"
Age: 70
'Retired'

Lire plus sur cette technique dans Charmant Python: Fonctionnelle Programmation en Python, Partie 1.

2
répondu sastanin 2013-03-21 10:24:17

Dans le code que vous avez posté la ligne suivante émule ternaire:

status = ("Working","Retired")[var>65]

Ici tuple ("Working","Retired") accessible avec un index [var>65], qui correspond à True (1) ou False (0). Quand il est accessible avec index 0, status sera 'Working'; Si index est 1 alors il sera "retiré". C'est une façon assez obscure de faire une affectation conditionnelle, utilisez la syntaxe ternaire normale qui a été introduite dans py2.5 comme cela a été dit.

0
répondu SilentGhost 2009-12-22 15:31:28

À l'origine, il N'y avait pas d'opérateur ternaire car "explicite vaut mieux qu'implicite", et il était considéré comme unpythonique. Je n'aime pas trop l'op ternaire de python, non plus, mais il existe:

x = foo if condition else bar

Comme le montre TM.

Comme pour status = ("Working","Retired")[var>65], var > 65 renvoie une valeur booléenne: True ou False); cependant, Python traite les types boolean assez faiblement: True est 1 et False est 0 dans certains contextes. Vous pouvez le vérifier en faisant >>> True == 1.

0
répondu Tordek 2009-12-22 15:32:52
status = ("Working","Retired")[var>65]

Cette ligne fonctionne comme un opérateur ternaire car l'expression var>65 renvoie 1 ou 0, selon que var est supérieur ou non à 65. Donc, si var>65, alors la ligne devient:

status = ("Working","Retired")[1]

C'est-à-dire le deuxième élément de la séquence ("Working","Retired"). Cela semble étrange mais pas si vous l'écrivez comme ceci à la place:

status_sequence = ("Working","Retired")
status = status_sequence[1]

, Donc status = "Retired".

De même, si var<=65 alors il devient

status = ("Working","Retired")[0]

Et status = "Working".

0
répondu Paul Stephenson 2009-12-22 15:34:53

Seule la ligne" status = " de ce code implémente quelque chose comme l'opérateur ternaire.

status = ("Working","Retired")[var>65]

Cela crée un tuple à deux éléments, avec des chaînes 'Working' à l'index 0, et 'Retired' à l'index 1. Après cela, il indexe dans ce tuple pour choisir l'un des deux éléments, en utilisant les résultats de l'expression var > 65.

Cette expression retournera True (équivalent à 1, choisissant ainsi 'Retired') si la valeur de var est supérieure à 65. Sinon, il retournera False (équivalent à 0, ainsi choisir "travail").

Il y a une différence clé entre cette approche et l'opérateur ternaire, bien que cela n'ait pas d'importance dans votre exemple particulier. Avec l'approche tuple-indexing, les deux valeurs sont évaluées mais une seule est renvoyée. Avec l'opérateur ternaire, une seule des deux valeurs est réellement évaluée; c'est ce qu'on appelle le comportement de "court-circuit". Cela peut avoir de l'importance dans des cas comme celui-ci:

status = funcA() if var > 65 else funcB()
status = (funcB(), funcA())[var > 65]

Dans le premier cas, funcA() est appelé ou funcB() est appelée, mais jamais les deux. Dans ce dernier cas, les deux sont appelés en premier, et les résultats sont stockés dans le tuple-alors un seul est choisi et le tuple est ignoré.

Ceci est particulièrement important pour comprendre si funcA() ou funcB() ont des "effets secondaires", ce qui signifie qu'ils modifient d'autres données au fur et à mesure de leur exécution.

0
répondu Peter Hansen 2009-12-22 15:35:37

En python 2.6 et plus:

print "You should be {0}.".format("retired" if var>65 else "working")

En python 3.1 et plus:

print ("You should be {}.".format("retired" if var>65 else "working"))
0
répondu Tim Pietzcker 2009-12-22 20:04:25

C'est la forme avec l'opérateur ternaire Python

def val():
    var = float(raw_input("Age:"))
    status = "Retired" if var > 65 else "Working"
    print "You should be:",status

Le code que vous avez montré est un peu délicat: il crée un tuple à deux éléments dont les éléments sont à la position 0 et 1. pour sélectionner le bon élément, il utilise une condition qui renvoie un booléen mais les booléens en python sont des entiers, vous pouvez donc l'utiliser comme index spéciaux (ils peuvent être 0 ou 1).

0
répondu mg. 2009-12-22 20:33:22

Essayer de donner une réponse complète basée sur les réponses données ici.

La façon dont vous avez trouvé (veuillez ne pas utiliser celui-ci car il n'est pas très lisible):

def val():
    var = float(raw_input("Age:"))
    status = ("Working","Retired")[var>65]
    print "You should be:",status

En utilisant la syntaxe Python 2.5+:

def val():
    var = float(raw_input("Age:"))
    status = "Working" if var>65 else "Retired"
    print "You should be:",status

En utilisant l'autre méthode commune encore préférée par certaines personnes:

def val():
    var = float(raw_input("Age:"))
    status = var>65 and "Working" or "Retired"
    print "You should be:",status

personnellement, j'ai tendance à utiliser la dernière puisque l'ordre des opérandes est le même que le C opérateur ternaire.

Modifier: trouvé quelques problèmes avec la dernière approche (Thx Roberto Bonvallet).
de wikipedia:

Ce code se casserait si op1 pouvait être un "falsy" valeur (Aucun, False, 0, un séquence ou collection vide,...) comme l'expression retournerait op2 (si c'était truthy ou falsy) au lieu du (faux) op1

Donc, ma dernière suggestion serait de utiliser l'opérateur ternaire 2.5 + car il est simple, lisible et offre un comportement de court-circuit.

0
répondu João Portela 2009-12-28 11:02:16