`assert` - warum?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

http://de.wikipedia.org/wiki/Assert

Hab eine Frage. Warum assert? Ist es nicht besser stattdessen eine "richtige" Exception auszulösen? Hab den oberen Artikel gelesen und soweit verstanden bloß frage ich mich nun wann assert angebracht ist und ob man das nicht zu oft benutzen sollte.

lg
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

sape hat geschrieben:Warum assert?
Hi sape!

``assert`` meldet mir meine eigenen persönlichen Fehler, die ich so beim Programmieren mache.

``assert`` hindert mich z.B. daran, die Zahl 100 zu übergeben, wenn ich nur eine Zahl zwischen 1 und 99 übergeben darf.

Viele dieser Prüfungen sind beim Programmieren noch recht wichtig, aber im fertigen Programm meist nicht mehr so sinnvoll. Ob jetzt der übergebene Parameter auch wirklich die eine oder andere Eigenschaft hat, ist für mich beim Programmieren wichtig. So merke ich frühzeitig, wenn ich etwas falsches übergebe.

``assert`` hat mich schon oft auf Fehler bei der Parameterübergabe aufmerksam gemacht. Deshalb setze ich es oft ein wenn die Qualität des Quellcodes sehr hoch sein muss und ich Fehler im Quellcode ziemlich früh bemerken möchte. Jetzt könnte man das typische DuckTyping-Argument nennen, aber ich denke, man muss immer abwiegen und selber entscheiden, ob eine eher lockere DuckTyping-Prüfung oder ein striktes ``isinstance`` angebracht ist.

``assert`` wird nicht ausgeführt, wenn Python mit ``-O`` gestartet wird. Man sollte aber auch im Hinterkopf behalten, dass die meisten Python-Programme NICHT mit ``-O`` ausgeführt werden.

Wenn man irgendwann alle ``assert``-Anweisungen los werden will, dann ist das mit einem Editor, der suchen kann, in wenigen Minuten erledigt.

``assert`` hat noch einen Vorteil: :D Man unterstützt dadurch die Code-Vervollständigung von WingIDE. Wenn WingIDE mal nicht weiß, was für ein Datentyp vorliegt und deshalb auch keine Code-Vervollständigung anbieten kann, dann hilft man einfach mit einem ``assert(isinstance())`` nach. Schon kann einem WingIDE beim Programmieren weiter helfen.

Fazit: ``assert`` hilft mir beim Programmieren Fehler zu vermeiden. ``assert`` ist nicht dazu gedacht, Benutzerfehler abzufangen.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

sape hat geschrieben:http://de.wikipedia.org/wiki/Assert

Hab eine Frage. Warum assert? Ist es nicht besser stattdessen eine "richtige" Exception auszulösen? Hab den oberen Artikel gelesen und soweit verstanden bloß frage ich mich nun wann assert angebracht ist und ob man das nicht zu oft benutzen sollte.
Mit `assert` testet man auf Sachen die unmöglich passieren können. Fehler in eigenem Quelltext, zum Beispiel Nachbedingungen oder Invarianten wie in dem Text ja auch beschrieben wird. Und es gibt auch keine sinnvolle Reaktion auf eine fehlgeschlagene Zusicherung. Man soll ja niemals nie sagen, aber `AssertionError` zu behandeln, ausser vielleicht um ein Errorlog oder so zu erstellen, ist ziemlich "falsch".

Beispiele:

In einem RLE-Packer der Daten für den C64 komprimiert:

Code: Alles auswählen

                    if self._runCount > 255:
                        assert self._runCount < 0x10000
                        result.append('\x00')
                        result.append(chr(self._runCount & 0xff))
                        result.append(chr((self._runCount >> 8) & 0xff))
Das mehr als 0xFFFF gleiche Bytes aufeinanderfolgen kann in dieser Methode eigentlich nie passieren. Das wird an anderer Stelle sichergestellt. Falls es aber doch passiert, dann ist dieses ``assert`` da und schlägt Alarm.

Aus einem Text-Parser der auf bestimmte Kommandozeilen reagiert:

Code: Alles auswählen

    def _get_commandline_count(self):
        """Calculates number of processed command lines.
        
        :returns: number of processed command lines.
        :rtype: int
        """
        result = self.line_number - self.textline_count
        assert result >= 0
        return result
Der Parser zählt die Textzeilen, also solche die kein Kommando enthalten, und die Gesamtzeilen. Diese Differenz darf nie negativ sein.
lunar

Zusammenfassung von mir:
  • Ausnahmen sind Fehler, die zur Laufzeit zwar nicht erwünscht, aber doch möglich sind, wie z.B. das Fehlen einer Datei; eben Ausnahmen vom normalen Programmablauf.
  • Mit assert Anweisungen dagegen sollten zur Laufzeit des Programmes niemals fehlschlagen. Tun sie es doch, ist das eben nicht nur eine Ausnahmesituation, sondern ein kapitaler Fehler in der Programmlogik.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Ich danke euch für die Erklärung. Ich werde das dann wohl wie bisher nicht nutzen, sondern nur Unittests weiter benutzen, weil es wohl eigentlich ausreichend ist.

lg
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

sape hat geschrieben:Ich werde das dann wohl wie bisher nicht nutzen
Assert nutzen auch nur wenige Programmierer und auch dann eher selten. In den Quellcodes die mir so untergekommen sinnd, waren nie viele Asserts drinnen. Ich selbst nutze keine - Fehler in der Programmlogik sind bei mir ja ausgeschlossen 8)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Das denke ich mir auch oft. Häufig ist ein ``assert`` im Quelltext die Folge eines Fehlers, den ich an der Stelle mal entdeckt habe. :-)
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Nehme alles zurück. Nun benutze ich seit heute auch assert in dem Parser, der den Python Quellencode in einem AST anbildet. Da ist es nützlich für mich stellen zu "markieren" die einen bestimmten Zustand niemals annehmen dürfe (vorher hatte ich dafür eine überprüfung gemacht und dann ne Exception geworfen). Da der Parser noch nicht fertig ist kommt das bei bestimmten Konstellationen zustande und ich habe dann ein gutes Indiz wo ich noch was vergessen habe (Wie z.B. die Docstrings die sich über mehrere Zeilen erstrecken.).

Dank eurer Tipps von gestern weiß ich nun, dass an solchen stellen wo von einem Programmfehler ausgegangen werden muss, keine Exceptions als "Marker" hingehören :) Danke nochmals für eure Tipps.

Aber noch eine Frage habe ich:
Nehmt ihr die assert Bedingungen wenn eurer Code Perfekt funktioniert wider weck oder lässt ihr die einfach drin? Ich hab mir überlegt wenn mein Parser zu 99% Fehlerfrei funktioniert, das ich die asserts auskommentieren, zwecks Performance. Oder sollte ich mir wegen der Performance keine großen Gedanken machen?

lg
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

sape hat geschrieben:Ich hab mir überlegt wenn mein Parser zu 99% Fehlerfrei funktioniert, das ich die asserts auskommentieren, zwecks Performance. Oder sollte ich mir wegen der Performance keine großen Gedanken machen?
Hast du deswegen Performanceprobleme? Nein? Dann lass sie drin. Ich habe Zweifel, ob der Performanceunterschied mit und ohne assert mehr als ein halbes Prozent ausmacht.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

OK, danke Leo, dann bleiben die drinnen :)

lg
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

asserts werden ignoriert, wenn man den Optimize Modus benutzt ;) Siehe http://www.python-forum.de/topic-7472.html

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:asserts werden ignoriert, wenn man den Optimize Modus benutzt ;) Siehe http://www.python-forum.de/topic-7472.html
Oder siehe den Thread hier, denn gerold hat dies schon ein paar Posts früher geschrieben :roll:
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Leonidas hat geschrieben:
jens hat geschrieben:asserts werden ignoriert, wenn man den Optimize Modus benutzt ;) Siehe http://www.python-forum.de/topic-7472.html
Oder siehe den Thread hier, denn gerold hat dies schon ein paar Posts früher geschrieben :roll:
Erstens, ich habe den post von Gerlod gelesen und zweitens, ich lese grundsätzlich alle posts (sogar mehrmals! Und das gilt auch für Threads bis ca. 20 Seiten.)! :evil:


Gerold hat ja auch geschrieben
Wenn man irgendwann alle ``assert``-Anweisungen los werden will, dann ist das mit einem Editor, der suchen kann, in wenigen Minuten erledigt.
.

Daher stellte ich die Frage...
Nehmt ihr die assert Bedingungen wenn eurer Code Perfekt funktioniert wider weck oder lässt ihr die einfach drin? Ich hab mir überlegt wenn mein Parser zu 99% Fehlerfrei funktioniert, das ich die asserts auskommentieren, zwecks Performance. Oder sollte ich mir wegen der Performance keine großen Gedanken machen?
, weil ich wissen wollte ob ihr asserts bei 100%ig funktionierenden Code entfernt weil...
a.) Sich die Performance erhöht? (ist ja nun geklärt -> ca. 0.5%)
b.) Um Toten-Code loszuwerden (ok, habe ich nicht erwähnt wo ich meinen post gerade lese ^^)
c.) Wegen anderen gründen?

So und nun Frieden meine Herren :D ;)

Um ein wenig Spaß reinzubringen: Nun sehe ich das ich assert zu oft nutze (komischerweise nur im Parser). Hab da jetzt ne Ladung die ich nach funktionierenden Code doch entsorgen werde ^^

:)

lg
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

sape hat geschrieben:a.) Sich die Performance erhöht? (ist ja nun geklärt -> ca. 0.5%)
Nein, natürlich nicht 0,5%! 0,5% war ein Schätzwert von mir, womit ich andeuten wollte, dass es sehr wenig sein wird. Wie viel Prozent es tatsächlich sind, müsstest du mit dem Profiler nachmessen. Aber große Unterschiede sind eher nicht zu erwarten. Außer du hast in jeder zweiten Zeile ein Assert und/oder der Assert-Code wird sehr oft ausgeführt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Es kommt letztendlich auch auf den Code an, der im ``assert`` ausgeführt wird. Da können schliesslich beliebige Ausdrücke stehen, auch solche die wirklich viel Code ausführen, eine Verbindung zu einer Datenbank aufbauen oder ähnliche Zeitfresser.

Zumindest für ``assert``s die Nachbedingungen oder Klasseninvarianten prüfen, kann man Unit-Tests schreiben. Dann geht der Test nicht ganz verloren, falls man das ``assert`` entfernt, sondern wird nur für ausgewählte Testfälle ausgeführt.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Leonidas hat geschrieben:oder der Assert-Code wird sehr oft ausgeführt.
Oh :shock: Hmm, wie schon geschrieben rede ich hier von einem Parser. Da kannst du dir ja vorstellen wie oft verschiedene Bereiche ausgeführt werden und zwar _sehr_ oft.

So sieht z.B. der Bereich, im `AstGenerator` aus der für die Funktions-/Klassendefinition zuständig ist:
http://paste.pocoo.org/show/501/

Wie gesagt, die Asserts sind für mich an diesen stellen Überlebenswichtig, um mich darauf hinzuweisen, wo eventuell noch was fehlt bzw. was ich nicht mitberücksichtigt habe zu Implementieren!

Wie ich schon geschrieben habe, sind alle Abschnitte (_add2node__XYZ) "irgendwie auch voneinander abhängig". Schlecht zu beschrieben, ohne näher auszuholen (könnte ich zwar jetzt mit einem Konkreten Beispiel machen (wo ncoh was fehlt) aber das würde den Rahmen hier sprengen.

Ich mache, wenn das Modul "ast.py" fertig ist, sowieso einen thread auf (PyMetric und PySource2UML).

Die runfunktion sieht so aus:
http://paste.pocoo.org/show/502/


Was ist nun für mich die Konsequenz mit den asserts die ja 1K mal aufgerufen werden? :? Doch auskommentieren oder?
EDIT:
-O ist keine Option, weil unter windows die Zeile #!/usr/bin/python -O nicht ausgewertet wird.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

...um noch einen drauf zu setzen.

Hier habe ich ein Codebeispiel, das ziemlich gut demonstriert, wofür *ich* ``assert`` meistens einsetze -- um Parameter auf Gültigkeit zu überprüfen. In diesem Beispiel (welches aus einer Klasse heraus gerissen wurde und deshalb auch auf Methoden zugreift, die nicht im Beispiel enthalten sind!) wird eine XML-Datei erstellt. Falls ein falscher Wert als Parameter übergeben wird, würde mein Programm zwar weiterhin korrekt funktionieren -- aber das Programm, dem die XML-Datei übergeben wird -- würde mir einen Kropf an den Hals wünschen. :-) Die Werte die als Parameter übergeben werden, werden nicht vom Benutzer übergeben, sondern vom Programmierer des Client-Programms. Ich fange also mögliche Programmierfehler per ``assert`` ab, um den Programmierer des Clientprogramms (das bin leider auch ich) frühzeitig auf falsch übergebene Parameter hinzuweisen.

Zur Erklärung der Methode:
In Zeile 65 ``(root, short_filename) = self._get_xml_base()`` wird bereits der Header der XML-Datei generiert. Der Rest des Codes fügt XML-Tags (nach Bedarf) hinzu. Die ``assert``-Anweisungen dienen zum Prüfen der übergebenen Parameter.
In Zeile 159 übergebe ich den XML-Root an eine Methode um daraus gültiges XML zu generieren und in eine Datei zu schreiben.
``et`` kommt von ``from elementtree import ElementTree as et``.

http://paste.pocoo.org/show/504/

Wenn jemand einen Fehler entdeckt --> bitte melden.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

sape hat geschrieben:Was ist nun für mich die Konsequenz mit den asserts die ja 1K mal aufgerufen werden? :? Doch auskommentieren oder?
Profiler anwerfen. Übrigens ist assert ein Keyword und keine Funktion, daher kannst du die Klammern weglassen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hier aml ein Screenshot von dem kleinen Tool mit dem ich überprüfen kann, ob alle "Abdrücke" im Python Sourcefile auch in den richtigen Knotenpunkten sind. Seit dem ich das für meine `AstGenerator` geschrieben hab, habe ich sehr viele Bugs beheben können :D BTW: Die grauen kästchen kann man mit einem Rechstklick auf einer Node öffne :D

Bild
Daraufklicken um zu einer vergrößerten Ansicht zu gelangen
BlackJack

sape hat geschrieben:
Leonidas hat geschrieben:oder der Assert-Code wird sehr oft ausgeführt.
Oh :shock: Hmm, wie schon geschrieben rede ich hier von einem Parser. Da kannst du dir ja vorstellen wie oft verschiedene Bereiche ausgeführt werden und zwar _sehr_ oft.
Das kommt darauf an was *sehr* oft bedeutet. Ein paar tausend mal muss nicht oft sein, auch ein paar zehntausend mal nicht. Wenn man nicht gerade einen universellen Parser schreibt, der O(n^3) Zeit benötigt, mit `n` der Anzahl der Eingabesymbole, dann sollten ein paar einfache ``assert``s pro verarbeitetem Symbol kein Problem sein.
Was ist nun für mich die Konsequenz mit den asserts die ja 1K mal aufgerufen werden? :? Doch auskommentieren oder?
1K ist grundsätzlich für einfache Prüfungen IMHO nicht viel. Du kannst den Code ja mal mit und ohne ``-O`` laufen lassen und vergleichen, ob sich das überhaupt bemerkbar macht. Und wenn es sich bemerkbar macht, dann musst Du entscheiden ob der Gewinn in der Geschwindigkeit gross genug ist, um auf die ``assert``s zu verzichten. Dann bleibt noch die Frage auf welche man verzichten möchte. Es gibt sicher welche, die wichtiger sind als andere und es gibt auch welche, die mehr zur Laufzeit beitragen als andere. Eins das das Programm 0.00001% langsamer macht muss man nicht unbedingt rauswerfen, andererseits kann ein Zeitfresser auch wichtig genug sein um ihn zu behalten.
Antworten