En Python, comment tester si une variable est None, True ou False

147

J'ai une fonction qui peut renvoyer l'une des trois choses suivantes:

  • succès ( True)
  • échec ( False)
  • erreur de lecture / analyse du flux ( None)

Ma question est la suivante: si je ne suis pas censé effectuer un test Trueou Falsecomment devrais-je voir quel est le résultat. Voici comment je le fais actuellement:

result = simulate(open("myfile"))
if result == None:
    print "error parsing stream"
elif result == True: # shouldn't do this
    print "result pass"
else:
    print "result fail"

est-ce vraiment aussi simple que de supprimer la == Truepartie ou devrais-je ajouter un type de données tri-bool. Je ne veux pas que la simulatefonction lève une exception car tout ce que je veux que le programme externe fasse avec une erreur est de l'enregistrer et de continuer.

James Brooks
la source
Vous posez la mauvaise question; vous devriez demander de l'aide pour définir votre résultat ... quelle est la différence que vous percevez entre "échec" et "flux d'analyse d'erreur", que signifient-ils, quelles sont les conséquences, quelle action l'appelant est susceptible de vouloir entreprendre dans chaque cas (réussite, échec, erreur d'analyse)?
John Machin
Je simule un système d'alimentation électrique, si les gens perdent l'électricité de leur maison, c'est une panne. Si je ne peux pas lire le fichier de simulation, c'est une erreur d'un type complètement différent.
James Brooks
2
À l'intérieur de la simulatefonction, j'attrape toutes les exceptions; Je ne veux pas que ce qui se passe dans le simulateur arrête le reste du programme en cours d'exécution (et le traitement de l'élément suivant). Mais les réponses me font changer d'avis.
James Brooks
1
@James Brooks: C'est vrai. C'est ce qu'est le traitement try / except. Si vous avez simulatedes choses qu'il peut attraper et réessayer, c'est bien. Mais s'il "échoue", il ne devrait pas revenir None. Il devrait simplement déclencher une exception au script qui l'a appelé. De toute façon, simulatec'est fait. Le retour Nonen'est pas aussi utile que de lever une exception appropriée - ou de permettre à une exception de se propager simulatedans le script appelant pour la gestion.
S.Lott
1
@James, utilisez except Exception:plutôt. Cela détecte toutes les erreurs «réelles», ainsi que Warninget StopIteration. Cela permet KeyboardInterruptet à SystemExittravers cependant. Si vous voulez vraiment les attraper, il est probablement préférable d'utiliser une autre structure externe try / except ou une autre structure qui documente clairement votre intention, car ce ne sont pas des "erreurs". (Mais j'ai dit "presque jamais" ... peut-être que dans votre cas, vous voulez vraiment tout saisir, et même empêcher Ctrl-C ou sys.exit()de sortir, etc.)
Peter Hansen

Réponses:

119

N'ayez pas peur de l'exception! Avoir votre programme simplement se connecter et continuer est aussi simple que:

try:
    result = simulate(open("myfile"))
except SimulationException as sim_exc:
    print "error parsing stream", sim_exc
else:
    if result:
        print "result pass"
    else:
        print "result fail"

# execution continues from here, regardless of exception or not

Et maintenant, vous pouvez avoir un type de notification beaucoup plus riche de la méthode de simulation quant à ce qui n'a pas fonctionné exactement, au cas où vous trouveriez que l'erreur / aucune erreur n'est pas suffisamment informative.

PaulMcG
la source
D'accord. Beaucoup plus pythonique que la solution évidemment plus populaire ci-dessus (qui sent trop le code C).
Brandon
7
@Brandon Pas d'accord. Ce code est plus long et, pire, moins lisible que la solution ci-dessus (ou la version améliorée ci-dessous): plus d'indentations, plus de déclarations différentes - devinez pourquoi cette dernière est plus populaire, comme vous le dites ... ;-) Pourquoi essayer de l'être «Pythonique» si cela conduit à un code plus gênant ...?
Rolf Bartstra
Maintenant, imprimez la traceback au lieu de "error parsing stream" et vous avez mon vote.
CivFan
Ok, tu as mon vote de toute façon, mais je voulais dire imprimer quelque chose comme traceback.format_exc() . Voir cette réponse SO.
CivFan
12
De nombreuses personnes viendront sur cette page à la recherche de la réponse à la question du titre. Pour la plupart d'entre nous "Ne craignez pas l'exception!" n'a rien à voir avec notre situation. Nous avons juste besoin de tester True, False et None. Bien que l'alternative que vous proposez soit valable dans certains cas, je pense qu'il est préférable d'inclure également une réponse à la question telle qu'elle a été posée.
vastlysuperiorman
163
if result is None:
    print "error parsing stream"
elif result:
    print "result pass"
else:
    print "result fail"

restez simple et explicite. Vous pouvez bien sûr prédéfinir un dictionnaire.

messages = {None: 'error', True: 'pass', False: 'fail'}
print messages[result]

Si vous prévoyez de modifier votre simulatefonction pour inclure plus de codes de retour, la maintenance de ce code peut devenir un peu un problème.

Le simulatepeut également soulever une exception sur l'erreur d'analyse, auquel cas vous l'attraperiez ici ou le laisseriez propager un niveau supérieur et le bit d'impression serait réduit à une instruction if-else d'une ligne.

SilentGhost
la source
1
Ce dernier est une sorte de test explicite contre Vrai ou Faux, n'est-ce pas?
Peter Eisentraut
1
bien sûr, mais sachant que ce ne sont que des valeurs de retour possibles, je ne pense pas que ce soit un problème.
SilentGhost
et cela semble être un peu plus rapide aussi
SilentGhost
a = 'foo' si a: print 'its true' a n'est pas réellement TRUE, ce n'est tout simplement pas aucun
wesm
17

Jamais, jamais, jamais dire

if something == True:

Jamais. C'est fou, car vous répétez de manière redondante ce qui est spécifié de manière redondante comme règle de condition redondante pour une instruction if.

Pire encore, jamais, jamais, jamais dire

if something == False:

Vous avez not. Sentez-vous libre de l'utiliser.

Enfin, faire a == Noneest inefficace. Faites a is None. Noneest un objet singleton spécial, il ne peut y en avoir qu'un. Vérifiez simplement si vous possédez cet objet.

S.Lott
la source
3
Tester l'égalité avec Truen'est pas redondant (même si je suis d'accord que ce n'est pas raisonnable). Il peut s'agir d'appeler une __eq__ou une autre méthode spéciale, qui pourrait pratiquement tout faire.
Scott Griffiths
5
@Scott Griffiths: Bon point. C'est un scénario vraiment et profondément horrible. Si c'est réellement le cas, le programme viole nos attentes fondamentales d'une manière qui en fait quelque chose qui doit être simplement supprimé et réécrit à partir de zéro sans une telle magie noire.
S.Lott
78
'Jamais jamais jamais' ...? Il existe cependant des cas qui if something == Truedonnent un résultat différent de celui if something, par exemple, pour les non-booléens something. 2==Truedonne faux alors que 2vaut vrai; None==Falseest faux mais not Noneest vrai!
Rolf Bartstra
9
-1 Cette réponse est trompeuse et complètement incorrecte, car ce que dit @Rolf Bartstra est vrai. Bien que dans ce cas, ce que vous dites peut être appliqué.
HelloGoodbye
3
-1. Depuis toute valeur non nulle, non vide ou non nulle pour les somethingretours Truesur bool(something). Dans ce cas, si vous voulez UNIQUEMENT vérifier si somethinga une valeur de Trueie bool. Ensuite, vous DEVEZ faire if something == TrueIMO.
Samarth Shah
2

Je voudrais souligner que, même s'il y a des situations où cela if expr :ne suffit pas parce que l'on veut être sûr que exprc'est Trueet pas seulement différent de 0/ None/ quoi que ce soit, il isfaut préférer == pour la même raison que S.Lott a mentionnée pour éviter== None .

Il est en effet un peu plus efficace et, cerise sur le gâteau, plus lisible par l'homme.

In [1]: %timeit (1 == 1) == True
38.1 ns ± 0.116 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [2]: %timeit (1 == 1) is True
33.7 ns ± 0.141 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Thrastylon
la source
1
Vous ne pouvez pas exécuter un benchmark une seule fois et dire que l'un est plus efficace que l'autre (même s'il peut l'être). Exécutez-le plusieurs fois (10 000) pour voir comment il se comporte en moyenne. \
user1767754
1

Je pense que jeter une exception est une meilleure idée pour votre situation. Une alternative sera la méthode de simulation pour renvoyer un tuple. Le premier élément sera le statut et le second le résultat:

result = simulate(open("myfile"))
if not result[0]:
  print "error parsing stream"
else:
  ret= result[1]
kgiannakakis
la source
1
renvoyer un tuple va généralement bien avec le déballage d'un tuple;)
SilentGhost
2
votre code, cependant, n'a pas beaucoup de sens, s'il Falseest renvoyé, il s'imprimera 'error parsing stream'.
SilentGhost
La méthode simulate doit renvoyer (False, "n'importe quoi du tout") ou (True, ret) où ret vaut False ou True.
kgiannakakis
2
Eh