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.
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.
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'
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"
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.
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.
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.
À 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
.
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"
.
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.
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"))
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).
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.