cookie: (PYTHON) Exception in __init__ - Objekt erzeugt oder nicht?

Hallo,

in den Quellen, die ich bisher nachgeschlagen habe, wird gesagt, dass beim Aufruf von __init__() in einer Klasse bereits ein Objekt erzeugt worden ist:

"(...) the object has already been constructed by the time __init__ is called, and you already have a valid reference to the new instance of the class."
(< http://diveintopython.org/object_oriented_framework/defining_classes.html#d0e11720>)

oder auch:

"The instantiation operation (``calling'' a class object) creates an empty object. Many classes like to create objects with instances customized to a specific initial state. Therefore a class may define a special method named __init__() ..."
(http://www.python.org/doc/current/tut/node11.html#SECTION0011320000000000000000)

wo zumindest gesagt wird, dass das Objekt mit der Instanziierung erzeugt wird und nicht mit der Methode __init__(). Diese wiederum sorgt nur für den gewünschten Anfangszustand.

Wenn nun also ein Objekt schon vor dem Aufruf der __init__()-Methode existiert, dann würde ich bei folgendem Programm:

---

  
class foo:  
    def __init__(self):  
        raise RuntimeError, 'Something went wrong'  
    def do_something(self):  
        print 'If you see this sentence, then an object was actually created.'  
  
try:  
    foo_object = foo()  
except RuntimeError, strerror:  
    print strerror  
  
foo_object.do_something()  

---

diese Ausgabe erwarten:

Something went wrong
If you see this sentence, then an object was actually created.

Stattdessen kommt aber:

Something went wrong
Traceback (most recent call last):
  File "foo.py", line 12, in <module>
    foo_object.do_something()
NameError: name 'foo_object' is not defined

Wie kommt das?

Viele Grüße
cookie

  1. Hallo Keks,

    Wie kommt das?

    Exceptions haben nun einmal die Eigenschaft einem aus dem normalen Steuerfluss zu -erheben-.

    NameError: name 'foo_object' is not defined

    In Deinem Fall wird die Initialisierung, angedacht im normalen Steuerfluß, durch -raise- nicht abgeschlossen.

    Gruß aus Berlin!
    eddi

    --
    Diese Nachricht ist made in Rixdorf und wurde mittels 100% recycelter Elektronen verfasst.
    Und der Strom? Äh - kommt aus der Steckdose! :)
    1. Danke für Deine Antwort, aber ich kapiere es trotzdem noch nicht.
      Nach den Links, die ich in meinem Ausgangsposting angegeben hatte, wird doch das Objekt schon *vor* der Ausführung von __init__ erzeugt. Oder stimmt das etwa nicht?
      Wenn das Objekt also schon existiert, dürfte es doch keine Rolle spielen, ob __init__ eine Ausnahme auslöst.

      In Deinem Fall wird die Initialisierung, angedacht im normalen Steuerfluß, durch -raise- nicht abgeschlossen.

      Okay, die Initialisierung wird nicht abgeschlossen, aber diese erfolgt doch erst *nach* der Objekterzeugung.
      Oder gibt es Python eine Regel (welche? Wo zu finden?), die besagt, dass bei nicht abgeschlossener Initialisierung das Objekt vom Python-Interpreter rückwirkend ungültig gemacht wird?

      Viele Grüße
      cookie

      1. Re:

          
        class foo:  
            def __init__(self,name):  
                self.name=name  
                self.do_something()  
                raise RuntimeError, 'Something went wrong'  
            def do_something(self):  
                print 'If you see this sentence, then an object was actually created.'  
          
        try:  
            foo_object = foo('huhu')  
        except RuntimeError, strerror:  
            print strerror  
        
        

        Okay, die Initialisierung wird nicht abgeschlossen, aber diese erfolgt doch erst *nach* der Objekterzeugung.

        Das ist das Eigenwillige daran. Das Objekt ist (bereits) erzeugt, beim Aufruf von __init__() sind andere Methoden, wie oben zu sehen, aufrufbar.
        Lustig - nicht?

        Gruß aus Berlin!
        eddi

        --
        Diese Nachricht ist made in Rixdorf und wurde mittels 100% recycelter Elektronen verfasst.
        Und der Strom? Äh - kommt aus der Steckdose! :)
        1. hallo,

          Okay, die Initialisierung wird nicht abgeschlossen, aber diese erfolgt doch erst *nach* der Objekterzeugung.
          Das ist das Eigenwillige daran. Das Objekt ist (bereits) erzeugt, beim Aufruf von __init__() sind andere Methoden, wie oben zu sehen, aufrufbar.
          Lustig - nicht?

          Naja, "lustig" - es ist zumindest etwas, was man von anderen Programmiersprachen in _dieser_ Form nicht kennt und was erheblich dazu beiträgt, daß Python noch eine Weile eine "Geheimsprache" bleiben wird - vorbehalten nur den "gelernten" Programmierern. Viele, die sich mit Python - aufgrund der scheinbaren Einfachheit seiner Syntax - beschäftigen möchten, scheitern an dieser Stelle bzw. an vergleichbaren Stellen und bauen dann das, was sie eben "bauen" möchten, mit einer Sprache, die sie eher "gewohnt" sind.

          Eigentlich schade. Python bietet eine Menge Möglichkeiten und überzeugt durch seine relativ simple Syntax. Aber es hält auch solche "Stolperstellen" bereit.

          Aber wer kann schon Python einsetzen? Was ich bei diversen Webhostern bisher gefunden habe, war allenfalls die Bereitstellung von ruby. Zeige mir mal einen, der es zuläßt, eine gesamte Webpräsenz auf Python aufzubauen - ich habe nur eine kleine Testversion auf meinem lokalen Apsche laufen, weil ich wissen wollte, ob das überhaupt geht (als Alternative zu PHP/Smarty). Geht. Nur: öffentlich einsetzbar ist es bei keinem Hoster, soweit mir bisher bekannt.

          Grüße aus der Nachbarschaft

          Christoph S.

          --
          Visitenkarte
          ss:| zu:) ls:& fo:) va:) sh:| rl:|
          1. echo $begrüßung;

            Naja, "lustig" - es ist zumindest etwas, was man von anderen Programmiersprachen in _dieser_ Form nicht kennt und was erheblich dazu beiträgt, daß Python noch eine Weile eine "Geheimsprache" bleiben wird - vorbehalten nur den "gelernten" Programmierern. Viele, die sich mit Python - aufgrund der scheinbaren Einfachheit seiner Syntax - beschäftigen möchten, scheitern an dieser Stelle bzw. an vergleichbaren Stellen und bauen dann das, was sie eben "bauen" möchten, mit einer Sprache, die sie eher "gewohnt" sind.

            Was ist mit denjenigen, die noch keine Programmiersprache kennen und an den Hürden einer x-beliebigen Sprache stolpern? Sie können beispielsweise nicht die Eigenheiten ihrer bereits erlernten Mutter- oder Fremdsprache auf die Programmiersprache übertragen. Und nun? Soll man nun alle Programmiersprachen bedauern?

            Eigentlich schade. Python bietet eine Menge Möglichkeiten und überzeugt durch seine relativ simple Syntax. Aber es hält auch solche "Stolperstellen" bereit.

            Diese Stolperstelle des OP ist keine Python-spezifische. Wenn ein Objekt während seiner Instantiierung eine Exception wirft und damit den aktuellen Fluss unterbricht kann eine anschließende Weiterverarbeitung des Objekte - und sei es nur eine Zuweisung zu einer Variablen - nicht mehr stattfinden. PHP macht das beispielsweise genauso.

            Aber wer kann schon Python einsetzen? [...] Nur: öffentlich einsetzbar ist es bei keinem Hoster, soweit mir bisher bekannt.

            1&1 - um nur mal einen zu nennen. Und das ist noch nicht mal ein kleiner.

            echo "$verabschiedung $name";

          2. Hallo Christoph,

            Das ist das Eigenwillige daran. Das Objekt ist (bereits) erzeugt, beim Aufruf von __init__() sind andere Methoden, wie oben zu sehen, aufrufbar.
            Lustig - nicht?

            Naja, "lustig" - es ist zumindest etwas, was man von anderen Programmiersprachen in _dieser_ Form nicht kennt

            Wie bitte?

            Konstruktoren funktionieren zum Beispiel in C++, Java und PHP exakt genau so: Der Speicherbereich für das Objekt ist schon alloziert und man kann im Konstruktor (unter Python heißt der halt __init__, unter PHP __construct und unter C++/Java so wie die Klasse selbst) bereits Methoden des Objekts aufrufen. Und wenn man in diesen Sprachen dann eine Exception wirft, dann wird das Objekt wieder gelöscht und man muss Exception Handling betreiben, d.h. der Konstruktor "schlägt fehl".

            Viele Grüße,
            Christian

          3. Hallo Christoph.

            Zeige mir mal einen, der es zuläßt, eine gesamte Webpräsenz auf Python aufzubauen

            Bei Occuris steht einem sowohl PHP als auch Perl und Python zur Verfügung, dazu SSH-Zugang und das ganze für wenig Geld.

            Servus,
            Flo

          4. Hallo Christoph,

            Dich hatte ich ja zwischenzeitlich wieder völlig vergessen. Ich kümmere mich heute um Deinen Artikel von der Serverkonfiguration.

            Gruß aus Berlin!
            eddi

            --
            Diese Nachricht ist made in Rixdorf und wurde mittels 100% recycelter Elektronen verfasst.
            Und der Strom? Äh - kommt aus der Steckdose! :)
            1. hallo,

              Dich hatte ich ja zwischenzeitlich wieder völlig vergessen.

              Ups. Wie kannst du nur, grrrr.

              Ich kümmere mich heute um Deinen Artikel von der Serverkonfiguration.

              Schön. Ich erwarte, wie per mail angesprochen, _fundierte_ Hinweise - und es ist nicht mehr sehr viel Zeit. Der selbst gewählte Veröffentlichungstermin für die Neufassung liegt auf dem 1. Juli ;-)

              Grüße aus Berlin

              Christoph S.

              --
              Visitenkarte
              ss:| zu:) ls:& fo:) va:) sh:| rl:|
        2. Ah, ich glaube, jetzt mit Deinem Beispiel wirds mir dann doch klar. Also, der Ablauf ist folgender:

          Im try-Block wird das Objekt foo_object erzeugt. Danach wird zwangsläufig __init__ aufgerufen. In diesem Moment existiert das Objekt, wie sich ja auch dadurch zeigt, dass in Deinem Beispiel die __init__-Methode die do_something-Methode erfolgreich aufruft.
          Dann wird die Exception ausgelöst. Der Python-Interpreter betrachtet den try-Block nun nicht mehr als den auszuführenden Zweig und macht die Anweisung rückgängig, durch die die Exception ausgelöst wurde (das erzeugte Objekt wird vernichtet). Dann wird der except-Zweig ausgeführt.

          Stimmt das so?

          Viele Grüße
          cookie

          class foo:
              def init(self,name):
                  self.name=name
                  self.do_something()
                  raise RuntimeError, 'Something went wrong'
              def do_something(self):
                  print 'If you see this sentence, then an object was actually created.'

          try:
              foo_object = foo('huhu')
          except RuntimeError, strerror:
              print strerror

          1. print begrüßung

            Im try-Block wird das Objekt foo_object erzeugt. Danach wird zwangsläufig __init__ aufgerufen. In diesem Moment existiert das Objekt, wie sich ja auch dadurch zeigt, dass in Deinem Beispiel die __init__-Methode die do_something-Methode erfolgreich aufruft.
            Dann wird die Exception ausgelöst. Der Python-Interpreter betrachtet den try-Block nun nicht mehr als den auszuführenden Zweig und macht die Anweisung rückgängig, durch die die Exception ausgelöst wurde (das erzeugte Objekt wird vernichtet). Dann wird der except-Zweig ausgeführt.
            Stimmt das so?

            Nicht ganz. Um Dinge rückgängig machen zu können müsste der vorherige Zustand irgendwo notiert werden. Das ist zu aufwendig um das automatisch und damit ständig zu machen. In deinem Fall wird das Objekt erzeugt und seine Initialisierung durchgeführt, aber die anschließende Zuweisung zur Variablen kann nicht mehr stattfinden, weil durch die Exception der Fluss unterbrochen wird. Als Lösung schlage ich vor, die von foo_object abhängigen Operationen in einem else-Zweig zu notieren.

            try:  
                foo_object = foo()  
            except RuntimeError, strerror:  
                print strerror  
            else:  
                foo_object.do_something()
            

            print "%s %s" % (verabschiedung, name)

            1. Hallo,

              danke für die Klärung. Das ganze hakt also in der Zuweisung. Klingt logisch. Die Konstruktion try-except-else habe ich jetzt auch nachgesehen; die war mir nicht bekannt.

              try:

              foo_object = foo()
              except RuntimeError, strerror:
                  print strerror
              else:
                  foo_object.do_something()

                
              Also wird die Methode do\_something nur dann aufgerufen, wenn die Zuweisung an foo\_object erfolgreich durchgeführt wurde.  
                
              Zusammengefasst:  
              try enthält den Code, der Ausnahmen auslösen könnte.  
              except enthält den Code, der ausgeführt wird, falls tatsächlich eine Ausnahme ausgelöst worden ist.  
              else enthält den Code, der nur dann ausgeführt wird, wenn keine Ausnahme ausgelöst worden ist.  
              finally enthält den Code, der auf jeden Fall nach dem except-Block bzw. else-Block ausgeführt wird, egal ob eine Ausnahme ausgelöst worden ist oder nicht.  
              Der Code, der eventuell auf die gesamte try-(except-else-finally)-Konstruktion folgt, wird dann ausgeführt, wenn keine Ausnahme ausgelöst worden ist oder die Ausnahme erfolgreich behandelt wurde.  
                
              Puh, ich hoffe, das stimmt jetzt so...  
                
              Nochmal vielen Dank an die Helfenden!  
                
              Viele Grüße  
              cookie
              
              1. echo $begrüßung;

                Die Konstruktion try-except-else habe ich jetzt auch nachgesehen; die war mir nicht bekannt.
                Zusammengefasst:
                try enthält den Code, der Ausnahmen auslösen könnte.
                except enthält den Code, der ausgeführt wird, falls tatsächlich eine Ausnahme ausgelöst worden ist.
                else enthält den Code, der nur dann ausgeführt wird, wenn keine Ausnahme ausgelöst worden ist.
                finally enthält den Code, der auf jeden Fall nach dem except-Block bzw. else-Block ausgeführt wird, egal ob eine Ausnahme ausgelöst worden ist oder nicht.

                Stimmt so. Das else in der try-Anweisung ist eine Python-Besonderheit. Vermutlich liegt sein Hauptzweck nur der übersichtlicheren Code-Notation, denn im Prinzip könnte man die abhängigen Zugriffe auch im try-Block notieren.

                try:
                    objekt = initialisierung
                    objekt.tuwas()
                  except ...:
                    pass

                Mir will irgendwie kein Anwendungsfall einfallen, der zwar mit else, nicht aber ohne es notiert werden kann. Deine erste These kann man auch so formulieren:
                try enthält den Code, der Ausnahmen auslösen könnte, und den Code, der von einer fehlerfreien Ausführung abhängig ist (sofern man ihn nicht unter else notiert).

                finally und else schließen sich übrigens gegenseitig aus, wenn man Python in einer Version kleiner als 2.5 verwendet.

                Der Code, der eventuell auf die gesamte try-(except-else-finally)-Konstruktion folgt, wird dann ausgeführt, wenn keine Ausnahme ausgelöst worden ist oder die Ausnahme erfolgreich behandelt wurde.

                Das Behandeln der Exception ist nebensächlich. Hauptsache ist, dass sie erfolgreich gefangen wurde (und nicht erneut oder irgendeine andere ausgelöst wurde).

                echo "$verabschiedung $name";

                1. Hallo,

                  danke für Deine Antwort.

                  Der Code, der eventuell auf die gesamte try-(except-else-finally)-Konstruktion folgt, wird dann ausgeführt, wenn keine Ausnahme ausgelöst worden ist oder die Ausnahme erfolgreich behandelt wurde.

                  Das Behandeln der Exception ist nebensächlich. Hauptsache ist, dass sie erfolgreich gefangen wurde (und nicht erneut oder irgendeine andere ausgelöst wurde).

                  Was ist der Unterschied zwischen behandeln und fangen?

                  Ich verstehe Dich jetzt folgendermaßen:

                  * behandeln: nach der Ausnahme im except-Block etwas Sinnvolles im Hinblick auf die Programmlogik tun

                  * fangen: Für die Ausnahme gibt es einen zugehörigen except-Block, der ausgeführt wird, womit die Ausnahme formal als erledigt betrachtet wird. Ob in diesem Block sinnvolle Programmanweisungen stehen, ist dabei unerheblich

                  Wenn also durch eine Ausnahme der except-Block ausgeführt wird (und eventuell danach ein finally-Block) dann wird anschließend der Code ausgeführt, der auf die gesamte try-(except-else-finally)-Konstruktion folgt - natürlich nur sofern bis dahin nicht wieder neue Ausnahmen ausgelöst worden sind.

                  Viele Grüße
                  cookie

                  1. echo $begrüßung;

                    Ich verstehe Dich jetzt folgendermaßen: [...]

                    Ja, so ist es richtig.

                    echo "$verabschiedung $name";

                    1. Okay, dann nochmal an Dich und die anderen Helfer ein ganz herzliches, abschließendes Dankeschön!

                      Viele Grüße
                      cookie