Seite 1 von 1

Python with-statement, Vergleich zu try-with-resources.

Verfasst: Donnerstag 5. Januar 2012, 16:28
von prog2012
Hallo!

Ich bin relativ neu in Phyton und habe eine Frage zu dem with-Befehl. Ich habe mir bereits http://www.python.org/dev/peps/pep-0343/ durchgelesen, es aber noch nicht verstanden.

Wenn ich soetwas wie

Code: Alles auswählen

with open() as h:
    for l in h:
        action(l)
schreibe. Ich möchte nicht auf das konkrete Beispiel eingehen, aber ist es möglich dass in der with-Zeile eine Exception und im Block darunter AUCH eine Exception geworfen wird?
Wenn ja wann wird welche Exception unterdrückt?

Ich versuche das with-statement mit dem try-with-resources statement aus Java zu vergleichen, weil sie doch ähnlich sind.

Re: Python with-statement, Vergleich zu try-with-resources.

Verfasst: Donnerstag 5. Januar 2012, 16:42
von EyDu
Hallo und willkommen in Forum.

Im Prinzip können sowohl in der with-Zeile als auch in dem dazugehörogen Block Exceptions geworfen werden. Unterdrückt wird eine Exception aber niemals. Ich erläutere es mal an deinem Beispiel. Kann zum Beispiel die bei open angegebene Datei nicht gefunden werden, so wird der Block gar nicht erst betreten und es wird eine IOException ausgelöst.

Angenommen, in deiner action-Methode wird ein IndexError geworfen, so sorgt das with-Statement lediglich dafür, dass die mittels open geöffnete Datei wieder geschlossen wird. Danach wird der IndexError jedoch erneut geworfen, so dass dieser außerhalb des with wieder zur Verfügung steht. Es wird also nichts unterdrückt, sondern nur für eine ordentliche Freigabe der Resourcen gesorgt.

Sebastian

Re: Python with-statement, Vergleich zu try-with-resources.

Verfasst: Donnerstag 5. Januar 2012, 16:55
von snafu
Bei Nutzung des `with`-Statements würde Python hier zunächst versuchen, die Datei zu öffnen. Schlägt dies fehl, dann wird eine Ausnahme geworfen und der Block wird nicht ausgeführt.

So, was passiert aber nach erfolgreichem Öffnen? Zunächst wird im Regelfall der Rückgabewert des `open()`-Aufrufs ja noch mittels `as` an einen Namen gebunden. Also z.B.:

Code: Alles auswählen

with open(filename) as stream:
[...]
Das ist bis zu dem Punkt noch nicht soviel anders als ein:

Code: Alles auswählen

stream = open(filename)
Der Unterschied ist bei `with` aber, dass nach Verlassen des Blocks ein Aufruf von `.close()` auf dem Objekt garantiert wird - egal ob innerhalb des Blocks eine Exception geworfen wurde oder ob alles gut gegangen ist. Im Grunde entspricht das Ganze einem:

Code: Alles auswählen

[...]
finally:
    stream.close()
Wenn du jetzt aber 100% äquivalenten Code schreiben würdest (also ohne `with`), dann müsstes du auch bedenken, dass es bei einem fehlerhaften `open()`-Aufruf gar nicht erst zum Erstellen von `stream` käme. Es würde dann nämlich dein `finally`-Block einen `NameError` werfen, da `stream` gar nicht bekannt wäre. Heißt also daher:

Code: Alles auswählen

try:
    stream = open(filename)
    # ein bißchen code ...
finally:
    try:
        stream.close()
    except NameError:
        pass
Und um dir das alles zu ersparen, wurde das `with`-Statement eingeführt, welches im Übrigen für alle möglichen Situationen verwendet werden kann, die irgendwie was mit Öffnen und Schließen zu tun haben (z.B. bei einer Verbindung zu einem Server und sowas).

Und ja, eine Ausnahme, die in dem `with`-Block auftritt, ist auch nach außen hin sichtbar.

Re: Python with-statement, Vergleich zu try-with-resources.

Verfasst: Donnerstag 5. Januar 2012, 19:26
von prog2012
Hey,

vielen Dank für eure schnellen Antworten. :-)

Was ist aber nun wenn ich das with-statement verwende,

und

1. im try-Block wird eine Exception geworfen.
2. close() wird aufgerufen und wirft auch eine Exception

Was passiert nun?? Bekomme ich beide Exceptions oder nur eine??



das with-statement soll ja wie folgt definiert sein, aber diese Definition verstehe ich nicht so richtig:

Code: Alles auswählen

   with EXPR as VAR:
            BLOCK
Definition:

Code: Alles auswählen

        mgr = (EXPR)
        exit = type(mgr).__exit__  # Not calling it yet
        value = type(mgr).__enter__(mgr)
        exc = True
        try:
            try:
                VAR = value  # Only if "as VAR" is present
                BLOCK
            except:
                # The exceptional case is handled here
                exc = False
                if not exit(mgr, *sys.exc_info()):
                    raise
                # The exception is swallowed if exit() returns true
        finally:
            # The normal and non-local-goto cases are handled here
            if exc:
                exit(mgr, None, None, None)

Re: Python with-statement, Vergleich zu try-with-resources.

Verfasst: Donnerstag 5. Januar 2012, 20:23
von /me
prog2012 hat geschrieben:das with-statement soll ja wie folgt definiert sein, aber diese Definition verstehe ich nicht so richtig:

Code: Alles auswählen

   with EXPR as VAR:
            BLOCK
Spiel einfach mal mit einer Klasse die selber das Context-Manager-Protokoll unterstützt.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import print_function
from __future__ import unicode_literals

class Data(object):
    def __init__(self, value):
        print('__init__')
        self.val = value

    def __enter__(self):
        print('__enter__')
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.val += 1
        print('__exit__')

    def __str__(self):
        return 'Data({})'.format(self.val)

    def do_something(self):
        self.val += 1


def use_data(value):
    bar = Data(value)
    bar.do_something()
    return bar

with Data(23) as foo:
    print(foo)

with use_data(41) as bar:
    print(bar)
print(bar)

Re: Python with-statement, Vergleich zu try-with-resources.

Verfasst: Donnerstag 5. Januar 2012, 21:12
von snafu
prog2012 hat geschrieben:Was ist aber nun wenn ich das with-statement verwende,

und

1. im try-Block wird eine Exception geworfen.
2. close() wird aufgerufen und wirft auch eine Exception

Was passiert nun?? Bekomme ich beide Exceptions oder nur eine??
Du bekommst nur eine Exception, und zwar die zuletzt geworfene. Kann man auch relativ einfach ausprobieren:

Code: Alles auswählen

class MyFile(file):
    def close(self):
        raise IOError('fake error')

with MyFile('test.txt') as f:
    f.spam()
Der Zugriff auf die unbekannte Methode `spam()` erzeugt zwar einen `AttributeError`, aber dieser wird überschrieben durch den `IOError`, weil `.close()` ja danach aufgerufen wird.

Re: Python with-statement, Vergleich zu try-with-resources.

Verfasst: Donnerstag 5. Januar 2012, 21:31
von EyDu
prog2012 hat geschrieben:Was ist aber nun wenn ich das with-statement verwende,

und

1. im try-Block wird eine Exception geworfen.
2. close() wird aufgerufen und wirft auch eine Exception

Was passiert nun?? Bekomme ich beide Exceptions oder nur eine??
Du kannst niemals zwei Exceptions bekommen. Wird eine Exception geworfen, wird sie so weit durchgereicht, bis sie behandelt wird. Also spätestens durch den Interpreter selbst, welcher sich dann beendet.

@snafu:
snafu hat geschrieben:Wenn du jetzt aber 100% äquivalenten Code schreiben würdest (also ohne `with`), dann müsstes du auch bedenken, dass es bei einem fehlerhaften `open()`-Aufruf gar nicht erst zum Erstellen von `stream` käme. Es würde dann nämlich dein `finally`-Block einen `NameError` werfen, da `stream` gar nicht bekannt wäre. Heißt also daher:

Code: Alles auswählen

try:
    stream = open(filename)
    # ein bißchen code ...
finally:
    try:
        stream.close()
    except NameError:
        pass
Schaue doch bitte noch einmal in die Definition, das von dir gezeigt Verhalten wird damit nicht geboten. Die EXPR wird VOR allen `try`s ausgeführt, es ist gar kein finally nötig. Es geht alleine um das Schließen der Datei (oder was auch sonst mit exit gemacht wird).