W Pythonie, jak powinienem sprawdzić, czy zmienna ma wartość None, True lub False

147

Mam funkcję, która może zwrócić jedną z trzech rzeczy:

  • sukces ( True)
  • awaria ( False)
  • błąd odczytu / analizy strumienia ( None)

Moje pytanie brzmi, czy nie mam testować przeciwko, Trueczy też Falsejak mam zobaczyć, jaki jest wynik. Poniżej przedstawiam, jak obecnie to robię:

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"

czy to naprawdę jest tak proste, jak usunięcie == Trueczęści, czy powinienem dodać typ danych tri-bool. Nie chcę, aby simulatefunkcja zgłaszała wyjątek, ponieważ jedyne, co chcę, aby program zewnętrzny zrobił z błędem, to zarejestrowanie go i kontynuowanie.

James Brooks
źródło
Zadajesz złe pytanie; powinieneś poprosić o pomoc w zdefiniowaniu wyniku ... jaka jest różnica, którą dostrzegasz między „niepowodzeniem” i „błędem podczas analizowania strumienia”, co one oznaczają, jakie są konsekwencje, jakie działanie może podjąć dzwoniący w każdym przypadku (pass, fail, parse error)?
John Machin,
Symuluję system elektroenergetyczny, jeśli ludzie tracą energię do swoich domów, jest to awaria. Jeśli nie mogę odczytać pliku symulacji, jest to błąd zupełnie innego rodzaju.
James Brooks
2
Wewnątrz simulatefunkcji łapię wszystkie wyjątki; Nie chcę, aby cokolwiek, co dzieje się w symulatorze, zatrzymywało działanie reszty programu (i przetwarzanie następnego elementu). Ale odpowiedzi sprawiają, że zmieniam zdanie.
James Brooks
1
@James Brooks: Racja. Na tym właśnie polega przetwarzanie prób / z wyjątkiem. Jeśli masz simulatecoś, co może złapać i spróbować ponownie, to dobrze. Ale jeśli „zawiedzie”, nie powinien powrócić None. Powinien po prostu zgłosić wyjątek do skryptu, który go wywołał. Tak czy inaczej, simulatejest skończone. Zwracanie Nonenie jest tak pomocne, jak zgłoszenie odpowiedniego wyjątku - lub zezwolenie na propagację wyjątku do simulateskryptu wywołującego w celu obsługi.
S.Lott,
1
@James, użyj except Exception:zamiast tego. To wychwytuje wszystkie „prawdziwe” błędy, wraz z Warningi StopIteration. Pozwala on KeyboardInterrupti SystemExitprzez chociaż. Jeśli naprawdę chcesz je złapać, prawdopodobnie najlepiej jest użyć innej, zewnętrznej try / z wyjątkiem lub innej struktury, która wyraźnie dokumentuje twoje zamiary, ponieważ nie są to „błędy”. (Ale powiedziałem "prawie nigdy" ... być może w twoim przypadku naprawdę chcesz złapać wszystko, a nawet uniemożliwić Ctrl-C lub sys.exit()wyjście z niego itp.)
Peter Hansen

Odpowiedzi:

119

Nie bój się Wyjątku! Wystarczy, że zalogujesz się i będziesz kontynuować program, tak łatwo, jak:

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

A teraz możesz mieć znacznie bogatszy typ powiadomienia z metody symulacji, co dokładnie poszło nie tak, w przypadku, gdy okaże się, że błąd / brak błędu nie jest wystarczająco informacyjny.

PaulMcG
źródło
Zgoda. Znacznie bardziej pythonowe niż ewidentnie bardziej popularne rozwiązanie powyżej (które pachnie zbyt podobnie jak kod C).
Brandon,
7
@Brandon Nie uzgodniono. Ten kod jest dłuższy i, co gorsza, mniej czytelny niż powyższe rozwiązanie (lub ulepszona wersja poniżej): więcej wcięć, więcej różnych zdań - zgadnij, dlaczego to drugie jest bardziej popularne, jak mówisz ... ;-) Po co próbować być „Pythonic”, jeśli prowadzi to do bardziej niezręcznego kodu…?
Rolf Bartstra
Teraz wydrukuj ślad zwrotny zamiast "Error parsing stream" i masz mój głos.
CivFan
Ok, i tak dostałeś mój głos, ale chodziło mi o wydrukowanie czegoś takiego traceback.format_exc() . Zobacz tę odpowiedź SO.
CivFan
11
Wiele osób przyjdzie na tę stronę w poszukiwaniu odpowiedzi na tytułowe pytanie. Dla większości z nas „Nie bój się Wyjątku!” nie ma nic wspólnego z naszą sytuacją. Musimy tylko przetestować Prawdę, Fałsz i Brak. Chociaż proponowana przez Ciebie alternatywa jest ważna w niektórych przypadkach, myślę, że najlepiej jest dołączyć również odpowiedź na zadane pytanie.
vastlysuperiorman
163
if result is None:
    print "error parsing stream"
elif result:
    print "result pass"
else:
    print "result fail"

niech to będzie proste i wyraźne. Możesz oczywiście wstępnie zdefiniować słownik.

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

Jeśli planujesz zmodyfikować swoją simulatefunkcję, aby zawierała więcej kodów powrotu, utrzymanie tego kodu może stać się nieco problemem.

simulateMoże również podnieść wyjątek na błąd analizy, w takim przypadku, że albo będzie go złapać tutaj albo niech propagują poziom w górę i nieco drukowanie zostanie zredukowana do jednej linii if-else.

SilentGhost
źródło
1
To ostatnie jest rodzajem wyraźnego testu przeciwko Prawdzie lub Fałszowi, prawda?
Peter Eisentraut
1
oczywiście, ale wiedząc, że są to tylko możliwe wartości zwracane, nie sądzę, żeby to był problem.
SilentGhost
i wydaje się być trochę szybszy
SilentGhost
a = 'foo' jeśli a: print 'to prawda' a nie jest w rzeczywistości PRAWDA, po prostu nie jest żadne
wesm
17

Nigdy, nigdy, nigdy nie mów

if something == True:

Nigdy. To szalone, ponieważ nadmiarowo powtarzasz to, co jest nadmiarowo określone jako nadmiarowa reguła warunku dla instrukcji if.

Gorzej, nigdy, nigdy, nigdy, nigdy nie mów

if something == False:

Masz not. Zapraszam do korzystania z niego.

Wreszcie działanie a == Nonejest nieefektywne. Zrób a is None. Nonejest specjalnym pojedynczym obiektem, może być tylko jeden. Po prostu sprawdź, czy masz ten przedmiot.

S.Lott
źródło
3
Testowanie równości z Truenie jest zbędne (chociaż zgadzam się, że nie jest to rozsądne). Może to być wywołanie jakiejś __eq__specjalnej metody, która może zrobić praktycznie wszystko.
Scott Griffiths
5
@Scott Griffiths: Słuszna uwaga. To naprawdę i głęboko przerażający scenariusz. Jeśli tak jest, program narusza nasze podstawowe oczekiwania w sposób, który sprawia, że ​​należy go po prostu usunąć i przepisać od nowa bez takiej czarnej magii.
S.Lott,
78
'Nigdy nigdy nigdy' ...? Są jednak przypadki, które if something == Truedają inny wynik niż if somethingnp. Dla wartości innych niż boolowskie something. 2==Truedaje false, podczas gdy zwraca 2wartość true; None==Falsejest fałszywe, ale not Noneprawdziwe!
Rolf Bartstra
9
-1 Ta odpowiedź jest myląca i całkowicie niepoprawna, ponieważ to, co mówi @Rolf Bartstra, jest prawdą. Chociaż w tym przypadku to, co mówisz, można zastosować.
HelloGoodbye
3
-1. Ponieważ każda wartość różna od zera lub niepusta lub niezerowa długość dla somethingzwraca Trueon bool(something). W takim przypadku, jeśli TYLKO chcesz sprawdzić, czy somethingma wartość Trueie bool. Następnie MUSISZ zrobić if something == TrueIMO.
Samarth Shah
2

Chciałbym podkreślić, że nawet jeśli są sytuacje, w których if expr :nie wystarczy, ponieważ chce się mieć pewność, że exprjest, Truea nie tylko różni się od 0/ None/ cokolwiek, isz == tego samego powodu, o którym wspomniał S.Lott, aby go unikać== None .

Jest rzeczywiście nieco wydajniejszy i - wisienka na torcie - bardziej czytelny dla człowieka.

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
źródło
1
Nie można raz uruchomić testu porównawczego i powiedzieć, że jeden jest bardziej wydajny niż drugi (choćby był). Uruchom go wiele razy (10.000), aby zobaczyć, jak zachowuje się średnio. \
user1767754
1

Uważam, że rzucenie wyjątku to lepszy pomysł na Twoją sytuację. Alternatywą będzie metoda symulacji zwracająca krotkę. Pierwsza pozycja będzie statusem, a druga wynikiem:

result = simulate(open("myfile"))
if not result[0]:
  print "error parsing stream"
else:
  ret= result[1]
kgiannakakis
źródło
1
powrocie krotka zwykle dobrze współgra z rozpakowaniem krotki;)
SilentGhost
2
Twój kod nie ma jednak większego sensu, jeśli Falsezostanie zwrócony, zostanie wydrukowany 'error parsing stream'.
SilentGhost
Metoda symulacji powinna zwrócić (False, „cokolwiek w ogóle”) lub (True, ret), gdzie ret ma wartość False lub True.
kgiannakakis
2
cóż, ponownie definiujesz wartości wyjściowe, aby pasowały do ​​Twojej logiki, nie jest to jasne bez wyjaśnienia
SilentGhost