@NoPy: Mir gefällt das.
Selbst wenn es einen Destruktor gäbe, auf den man sich verlassen könnte, dass er wenigstens auf jeden Fall überhaupt aufgerufen wird, hätte ``with`` immer noch den Vorteil das ich weiss wie lange der Kontextmanager ”aktiv” ist. Und das `__exit__()` auch an der Stelle aufgerufen wird, selbst wenn ich noch laaaaange danach irgendwo eine Referenz auf das Objekt habe. Ausserdem ist `__exit__()` ja nicht immer gleichbedeutend mit dem ”tot” des Objekts. Es gibt auch Kontextmanager bei denen man sinnvoll mehrfach ``with`` anwenden kann. Sperren für nebenläufige Programmierung beispielsweise, da wäre es sogar sinnfrei wenn das Objekt nur ein ``with`` ”überlebt”. Auch Dein Objekt könnte man nach einem ``with``-Block ja noch mal in einem Weiteren verwenden.
Das `nimm_dies()` ohne Ausnahme verlassen wird kann man im allgemeinen Fall nicht garantieren. Nur in dem man dort wirklich alle Ausnahmen behandelt, also ist die Frage ob man dort überhaupt alle *sinnvoll* behandeln kann, oder ob bestimmte nicht weiter oben in der Aufrufhierarchie behandelt werden können/sollten. Wenn man das so machen würde, müsste man an allen Stellen wo dieses Muster vorkommt den ”Fluss” von Ausnahmen stoppen. Oder eben mindestens jedes mal konsequent ein ``try``/``finally`` für die Aufräumarbeiten einfügen. Was kaum jemand macht, weil einem das auch nirgends so wirklich gezeigt/eingebläut wird. Eigentlich müssten ja alle Programme die eine Datei öffnen und etwas damit machen so aussehen:
Code: Alles auswählen
lines = open('test.txt')
try:
for line in lines:
do_something(line)
finally:
lines.close()
Da schlampen aber so ziemlich alle Programmierer gerne (ich auch bis es ``with`` gab!) und Anfänger sehen so etwas nicht in freier Wildbahn, und in Tutorials kommen in der Regel Dateien in Beispielen bevor Ausnahmebehandlung gezeigt wird.
In Python kann ein Objekt nicht so einfach eigenverantwortlich hinter sich aufräumen weil es keine verlässlichen Garantien gibt wann und ob die Speicherbereinigung das Objekt abräumt. Diese Garantien gibt es nicht um die Art der Speicherverwaltung nicht festzulegen. Die ist bei CPython anders als bei Jython und anders als bei IronPython. Bei .NET weiss ich es jetzt nicht, aber die JVM hat da ja einen ähnlichen Ansatz. Da gibt's auch `Object.finalize()` von dessen Implementierung abgeraten wird, weil das nicht wirklich deterministisch ist.
Den letzten Absatz habe ich nicht ganz verstanden‽ Damit sich das Objekt nicht verabschiedet reicht es das erreichbar zu halten. Da es ja anscheinend *gebraucht* wird, muss es das ja auch sein solange es gebraucht wird. Das wird sich also nicht einfach so verabschieden können, in dem Sinne das vor Ende des Vorgangs *weg* ist. Ist es ja nach Ende des ``with``-Blocks auch nicht. Das kann sowohl vor als auch nach dem Block problemlos weiter existieren wie jedes andere Objekt auch. Einfaches Beispiel dafür wären wieder Sperrobjekte.
Ganz allgemein können Kontextmanager für ”symmetrische” Aufrufe verwendet und haben erst einmal überhaupt nichts mit der Lebenszeit des Objekts zu tun. Das sorgt erst einmal nur dafür das zu jedem a() am Anfang des Blocks auch ein b() am Ende des Blocks erfolgt, egal warum der Block verlassen wird. Also `open()`/`close()`, `aquire()`/`release()`, `start_tag()`/`end_tag()`, und so weiter. a() und b() *können* die einzig ”nützliche” Lebenszeit eines Objekts betreffen, müssen das aber nicht.