Wyłącz potwierdzenia w Pythonie

90

Jak wyłączyć asercje w Pythonie?

Oznacza to, że jeśli twierdzenie zawiedzie, nie chcę, aby rzucało AssertionError, ale kontynuowało.

Jak mogę to zrobić?

Claudiu
źródło

Odpowiedzi:

75

Jak wyłączyć asercje w Pythonie?

Istnieje wiele podejść, które mają wpływ na pojedynczy proces, środowisko lub pojedynczą linię kodu.

Demonstruję każdego.

Na cały proces

Użycie -Oflagi (duże O) wyłącza wszystkie instrukcje assert w procesie.

Na przykład:

$ python -Oc "assert False"

$ python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError

Zauważ, że wyłączając mam na myśli, że nie wykonuje również następującego wyrażenia:

$ python -Oc "assert 1/0"

$ python -c "assert 1/0"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

Dla środowiska

Możesz również użyć zmiennej środowiskowej, aby ustawić tę flagę.

Wpłynie to na każdy proces, który wykorzystuje lub dziedziczy środowisko.

Np. W systemie Windows ustawienie, a następnie wyczyszczenie zmiennej środowiskowej:

C:\>python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError
C:\>SET PYTHONOPTIMIZE=TRUE

C:\>python -c "assert False"

C:\>SET PYTHONOPTIMIZE=

C:\>python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError

To samo w systemie Unix (używanie set i unset dla odpowiednich funkcji)

Pojedynczy punkt w kodzie

Kontynuuj swoje pytanie:

jeśli asercja się nie powiedzie, nie chcę, aby wyrzucała AssertionError, ale kontynuowała.

Jeśli chcesz, aby kod, którego nie udało się wykonać, możesz złapać albo upewnić się, że przepływ sterowania nie dociera do potwierdzenia, na przykład:

if False:
    assert False, "we know this fails, but we don't get here"

lub możesz złapać błąd asercji:

try:
    assert False, "this code runs, fails, and the exception is caught"
except AssertionError as e:
    print(repr(e))

który drukuje:

AssertionError('this code runs, fails, and the exception is caught')

i będziesz kontynuować od momentu, w którym zajmowałeś się AssertionError.

Bibliografia

Od tej assertdokumentacji :

Oświadczenie asertujące, takie jak to:

assert expression #, optional_message

Jest równa

if __debug__:
    if not expression: raise AssertionError #(optional_message)

I,

zmienna wbudowana __debug__występuje Truew normalnych okolicznościach, Falsegdy żądana jest optymalizacja (opcja wiersza poleceń -O).

i dalej

Przydziały do __debug__są nielegalne. Wartość zmiennej wbudowanej jest określana podczas uruchamiania interpretera.

Z dokumentacji użytkowania:

-O

Włącz podstawowe optymalizacje. Zmienia to rozszerzenie nazwy pliku skompilowanych (kod bajtowy) z .pyc na .pyo. Zobacz także PYTHONOPTIMIZE.

i

PYTHONOPTYMALIZACJA

Jeśli jest ustawiony na niepusty łańcuch, jest to równoważne określeniu -Oopcji. Jeśli jest ustawiona na liczbę całkowitą, jest to równoważne określaniu -Owielokrotności.

Aaron Hall
źródło
czy można by pominąć kod, który zawodzi w przypadku „pojedynczego punktu w kodzie”? Próbowałem ustawić __debug__Fałsz, ale nie jest to dozwolone.
Matthijs
1
@Matthijs możesz albo upewnić się, że przepływ sterowania nie dotrze do niego (np. if False: assert False), Albo możesz złapać błąd Assertion. To są twoje wybory. Zaktualizowałem odpowiedź, aby odpowiedzieć na Twoje pytanie.
Aaron Hall
Dzięki za odpowiedź, ale jeszcze nie do końca o czym myślałem. Chciałbym wyłączyć twierdzi wewnątrz funkcji podczas wykonywania, najlepiej z jakiegoś menedżera kontekstowego: twierdzenie jest oceniany: foo()i twierdzenia Wyłączanie: with skip_assertion(): foo(). Zaletą tego jest to, że nie muszę dodawać kolejnej flagi do funkcji
Matthijs
2
Możesz przepisać kod bajtowy funkcji, przepisać AST lub przepisać samą funkcję. (ręcznie lub programowo, w obu przypadkach). Przepisanie AST byłoby prawdopodobnie najbardziej niezawodnym podejściem („po prostu” zastąpienie Assertobiektów Passobiektami). Menedżer kontekstu nie działałby bezpośrednio w tym celu, ale mógłbyś mieć jakiś mechanizm, który używałby dekorowanych funkcji w ten sposób. Mimo wszystko nie polecam tego. Podejrzewam, że powodem, dla którego chcesz to zrobić, jest wywołanie kodu, którego nie kontrolujesz, i pobranie AssertionErrors. Jeśli tak, prawdopodobnie musisz znaleźć inną poprawkę.
Aaron Hall
59

Wywołaj Pythona z flagą -O:

test.py:

assert(False)
print 'Done'

Wynik:

C:\temp\py>C:\Python26\python.exe test.py
Traceback (most recent call last):
  File "test.py", line 1, in <module>
    assert(False)
AssertionError

C:\temp\py>C:\Python26\python.exe -O test.py
Done
Mark Rushakoff
źródło
8
Assert nie jest funkcją, więc pareny są zbędne.
Aaron Hall
15

Obie podane już odpowiedzi są poprawne (wywołaj Pythona z linii poleceń -Olub -OOz wiersza poleceń).

Oto różnica między nimi:

  • -OWłącz podstawowe optymalizacje. Zmienia to rozszerzenie nazwy pliku skompilowanych (kod bajtowy) z .pyc na .pyo.

  • -OOOprócz-O optymalizacji należy odrzucić ciągi dokumentacyjne .

(Z dokumentacji Pythona )

Michael Currie
źródło
7

Zastosowanie python -O:

$ python -O
>>> assert False
>>> 
John Millikin
źródło
3

NIE powinieneś wyłączać (większości) asercji. Wyłapują nieoczekiwane błędy, gdy uwaga jest gdzie indziej. Zobacz Regułę 5 w rozdziale „Siła dziesięciu” .

Zamiast tego chroń niektóre drogie testy potwierdzeń, wykonując takie czynności jak:

import logging
logger = logging.getLogger(__name__)

if logger.getEffectiveLevel() < logging.DEBUG:
    ok = check_expensive_property()
    assert ok, 'Run !'

Jednym ze sposobów zachowania ważnych stwierdzeń i umożliwienia assertoptymalizacji instrukcji jest podniesienie ich w instrukcji selekcji:

if foo_is_broken():
    raise AssertionError('Foo is broken!')
Ioannis Filippidis
źródło
1
//, Problem polega jednak na tym, że instrukcja nadal zwiększa cykliczną złożoność, a obsługa błędów powinna zająć się resztą?
Nathan Basanese
1
Twierdzenia, które byłyby strzeżone jak powyżej, to kosztowne wywołania, które znacznie spowalniają wykonanie. W przypadku niektórych algorytmów tego rodzaju kontrole mogą trwać o rząd wielkości dłużej niż cały program. Pomyśl o uruchomieniu naiwnej, ale prostszej implementacji (więc mniej prawdopodobne, że będzie zawierała błędy) tego samego algorytmu, aby sprawdzić poprawność. Lub sprawdzenie poprzez wyczerpujące wyliczenie czegoś, co nie wchodzi w grę w normalnym działaniu.
Ioannis Filippidis
Nie widzę większego problemu z czytelnością, ponieważ taka instrukcja nie dodaje zagnieżdżenia do kodu. Wyodrębnienie go jako wywołania funkcji może usunąć go z drogi, jeśli jest to problem (i spodziewam się, że taka refaktoryzacja powinna zmniejszyć złożoność cykliczną). W każdym razie cykliczna złożoność nie powinna regulować kontroli bezpieczeństwa.
Ioannis Filippidis
2

Uruchomienie w trybie zoptymalizowanym powinno to zrobić:

python -OO module.py
FogleBird
źródło