Deaktivieren Sie Zusicherungen in Python

Antworten:

68

Wie deaktiviere ich Zusicherungen in Python?

Es gibt mehrere Ansätze, die sich auf einen einzelnen Prozess, die Umgebung oder eine einzelne Codezeile auswirken.

Ich demonstriere jeden.

Für den gesamten Prozess

Die Verwendung des -OFlags (Großbuchstabe O) deaktiviert alle Assert-Anweisungen in einem Prozess.

Beispielsweise:

$ python -Oc "assert False"

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

Beachten Sie, dass mit deaktivieren gemeint ist, dass der darauf folgende Ausdruck auch nicht ausgeführt wird:

$ 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

Für die Umwelt

Sie können dieses Flag auch mit einer Umgebungsvariablen setzen.

Dies wirkt sich auf jeden Prozess aus, der die Umgebung verwendet oder erbt.

Beispiel: Setzen und löschen Sie in Windows die Umgebungsvariable:

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

Gleiches unter Unix (mit set und unset für die jeweilige Funktionalität)

Einzelner Punkt im Code

Sie setzen Ihre Frage fort:

Wenn eine Assertion fehlschlägt, möchte ich nicht, dass sie einen AssertionError auslöst, sondern dass sie weitergeht.

Wenn Sie möchten, dass der Code nicht ausgeführt wird, können Sie entweder sicherstellen, dass der Kontrollfluss die Behauptung nicht erreicht, zum Beispiel:

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

oder Sie können den Assertionsfehler abfangen:

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

welche druckt:

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

und Sie werden von dem Punkt an weitermachen, an dem Sie das erledigt haben AssertionError.

Verweise

Aus der assertDokumentation :

Eine Aussage wie diese:

assert expression #, optional_message

Ist äquivalent zu

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

Und,

Die integrierte Variable __debug__befindet sich Trueunter normalen Umständen, Falsewenn eine Optimierung angefordert wird (Befehlszeilenoption -O).

und weiter

Aufträge an __debug__sind illegal. Der Wert für die integrierte Variable wird beim Start des Interpreters festgelegt.

Aus den Verwendungsdokumenten:

Aktivieren Sie grundlegende Optimierungen. Dadurch wird die Dateinamenerweiterung für kompilierte (Bytecode-) Dateien von .pyc in .pyo geändert. Siehe auch PYTHONOPTIMIEREN.

und

PYTHONOPTIMIEREN

Wenn dies auf eine nicht leere Zeichenfolge festgelegt ist, entspricht dies der Angabe der -OOption. Wenn eine Ganzzahl festgelegt ist, entspricht dies einer -Omehrfachen Angabe .

Aaron Hall
quelle
Wäre es möglich, den Code zu überspringen, der im Fall von "Einzelpunkt im Code" fehlschlägt? Ich habe versucht, __debug__auf False zu setzen, aber das ist nicht erlaubt.
Matthijs
1
@Matthijs Sie können entweder sicherstellen, dass der Kontrollfluss ihn nicht erreicht (z. B. if False: assert False), oder Sie können den Assertion-Fehler abfangen. Das sind deine Entscheidungen. Die Antwort wurde aktualisiert, um Ihre Frage zu beantworten.
Aaron Hall
Danke für die Antwort, aber noch nicht ganz das, worüber ich nachgedacht habe. Ich möchte Asserts innerhalb einer Funktion zur Laufzeit deaktivieren, idealerweise mit einer Art Kontextmanager: Assertion wird ausgewertet: foo()und Assertions ausschalten : with skip_assertion(): foo(). Der Vorteil davon ist, dass ich kein weiteres Flag auf der Funktion hinzufügen muss
Matthijs
2
Sie können den Bytecode der Funktion neu schreiben, den AST neu schreiben oder die Funktion selbst neu schreiben. (entweder manuell oder programmgesteuert). Das Umschreiben des AST wäre wahrscheinlich der zuverlässigste Ansatz ("einfach" AssertObjekte durch PassObjekte ersetzen ). Ein Kontextmanager würde dafür nicht direkt arbeiten, aber Sie könnten einen Mechanismus haben, der dekorierte Funktionen auf diese Weise verwendet. Trotzdem empfehle ich es nicht. Ich vermute, Ihr Grund dafür ist, dass Sie Code aufrufen, den Sie nicht kontrollieren, und AssertionErrors erhalten. In diesem Fall müssen Sie wahrscheinlich einen anderen Fix finden.
Aaron Hall
58

Rufen Sie Python mit dem Flag -O auf:

test.py:

assert(False)
print 'Done'

Ausgabe:

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
quelle
7
Assert ist keine Funktion, daher sind die Parens überflüssig.
Aaron Hall
15

Beide bereits gegebenen Antworten sind gültig (rufen Sie Python entweder mit -Ooder -OOüber die Befehlszeile auf).

Hier ist der Unterschied zwischen ihnen:

  • -OAktivieren Sie grundlegende Optimierungen. Dadurch wird die Dateinamenerweiterung für kompilierte (Bytecode-) Dateien von .pyc in .pyo geändert.

  • -OOVerwerfen Sie zusätzlich zu den -OOptimierungen Dokumentzeichenfolgen .

(Aus der Python-Dokumentation )

Michael Currie
quelle
7

Verwendung python -O:

$ python -O
>>> assert False
>>> 
John Millikin
quelle
3

Sie sollten (die meisten) Zusicherungen NICHT deaktivieren. Sie fangen unerwartete Fehler auf, wenn die Aufmerksamkeit woanders liegt. Siehe Regel 5 in "Die Zehnerpotenz" .

Bewachen Sie stattdessen einige teure Assertionsprüfungen wie folgt:

import logging
logger = logging.getLogger(__name__)

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

Eine Möglichkeit, wichtige Aussagen beizubehalten und die assertOptimierung von Anweisungen zu ermöglichen, besteht darin, sie in einer Auswahlanweisung anzuheben:

if foo_is_broken():
    raise AssertionError('Foo is broken!')
Ioannis Filippidis
quelle
1
//, Das Problem ist jedoch, dass die Anweisung immer noch zur zyklomatischen Komplexität beiträgt und die Fehlerbehandlung den Rest erledigen sollte.
Nathan Basanese
1
Die Behauptungen, die wie oben geschützt werden würden, sind teure Anrufe, die die Ausführung erheblich verlangsamen. Bei einigen Algorithmen können Überprüfungen dieser Art um Größenordnungen länger dauern als das gesamte Programm. Stellen Sie sich vor, Sie führen eine naive, aber einfachere Implementierung (die weniger Fehler enthält) desselben Algorithmus aus, um die Richtigkeit zu überprüfen. Oder eine Überprüfung durch erschöpfende Aufzählung von etwas, das für den normalen Betrieb nicht in Frage kommt.
Ioannis Filippidis
Ich sehe kein großes Problem mit der Lesbarkeit, da eine solche Anweisung dem Code keine Verschachtelung hinzufügt. Das Extrahieren als Funktionsaufruf kann es aus dem Weg räumen, wenn dies ein Problem darstellt (und ich gehe davon aus, dass ein solches Refactoring die zyklomatische Komplexität verringern sollte). In jedem Fall sollte die zyklomatische Komplexität die Sicherheitsüberprüfungen nicht regeln.
Ioannis Filippidis
2

Das Ausführen im optimierten Modus sollte dies tun:

python -OO module.py
FogleBird
quelle