Variablen automatisch sichern / Löschmethode überschreiben

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.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Auf Python-Ebene sollte man definitiv nicht in die Speicherverwaltung pfuschen. Inwiefern es in der C-Schnittstelle irgendwelche Finalizer-Funktionen gibt, wo man sich einhängen könnte, weiß ich nicht. Der mir richtig erscheinende Ansatzpunkt wäre ja jetzt eher, nach einem Debugger Ausschau zu halten, der die gewünschte Funktionalität beherrscht. Einen alternativen GC zu suchen, wäre auch eine Idee, wobei ich kaum glaube, dass es so etwas gibt. Da wäre die Nutzung von alternativen Python-Implementierungen (z.B. PyPy) wohl zielführender. Sind aber nur ein paar Denkanstöße. Ich könnte nicht sagen, wie man das meinetwegen mit PyPy hinbekommen würde.

EDIT: Für CPython gibt es wohl tp_dealloc, aber auch dies dürfte wenig hilfreich sein, da das Problem mit der Reihenfolge damit wohl nicht gelöst ist. Es wurde ja bereits gezeigt, dass nicht zwangsläufig das zuletzt erstellte Objekt auch als letztes zerstört wird und mit `del` nur das Löschen aus dem Namensraum kontrolliert werden kann. Insofern ist die Namensgleichheit von `del` und `__del__()` natürlich schon recht irreführend. Sicher handelt der GC in seinem Vorgehen nach irgendwelchen festen Regeln. Diese zu durchschauen, ist aber ziemlich kompliziert und führt sicher nicht zu lesbarem und leicht wartbarem Code. ;)
lunar

@snafu: Die alternativen Python-Implementierung pypy, jython und IronPython sind für dieses Vorhaben noch weniger geeignet, da der GC bei diesen Implementierungen tatsächlich nicht deterministisch ist. Dort gibt es noch weniger Garantien hinsichtlich des Aufrufs von ".__del__()".

@Gerenuk: Die Lösung auf StackOverflow hat auch ihre Probleme. Solche Exemplare kannst Du beispielsweise nicht mehr einfach pickeln, weil der Typ dynamisch erzeugt ist, und somit beim Unpickle gar nicht mehr zur Verfügung steht.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nochmal zum generellen Ansatzpunkt: Ich halte es für falsch, den Wert vom Attribut eines Objektes erst in dem Moment abzufragen, wenn dieses gelöscht wird. Was man doch eigentlich will, ist ein Tracking der Wertveränderungen. Jedes Objekt implementiert seinen Namensraum in einer eigenen `.__dict__()`-Methode. Wenn man also nun ein Objekt an die Überwacher-Methode gibt, muss man nur noch die Veränderungen im jeweiligen Wörterbuch prüfen und dies dann protokollieren. Unter Zuhilfenahme des Moduls `inspect` könnte man dann detailliert alle Attributsveränderungen und zugehörigen Funktionsaufrufe mitsamt der verwendeten Argumente zurückverfolgen.
Gerenuk
User
Beiträge: 69
Registriert: Donnerstag 21. Januar 2010, 22:27

@pillmuncher:
Die Lösung von Boaz zusammen mit gc.collect() funktioniert und erfüllt die Aufgabe :)

Da jetzt Fälle konstruieren die in meinen Programmcode eh nicht vorkommen zeigt nur Neid ;) Klar, könnte ein Komet mein Haus zerstören. Dann bräuchte ich eine vernetzte Lösung, die gegen Ausfälle sicher ist. Es könnte ganz vieles - tut es aber nicht. Das Result der Ausgabe ist Debug-Info für mich und nicht die Steuerung der russischen Raketen. Selbst wenn eine zirkuläre Referenz reinkommt, dann werde ich das schon feststellen und meistens wird das nicht passiert. Von einigen wichtigen Variablen kann ich das mit Sicherheit sagen.
Ich habe auch schon geschrieben, das es um die Fälle geht wo das Programm nicht abschmiert. Und das die Reihenfolge nicht definiert ist, ist ja klar und überhaupt die Notwendigkeit dieser ganzen Übung weil ich eben nicht weiß wann die gelöscht werden um da ein print einzubauen.

@lunar:
Das mit dem picklen geht wahrscheinlich wirklich nicht - stimmt, aber wie gesagt soll das ein optionales Debugfeature sein. Es funktioniert besser als wenn ich tief in meinen Code logging einbauen muss, dass ich nicht wieder rausbekomme wenn es mal weg soll. Deswegen auch der Versuch nur von außen die Manipulation zu machen.

@snafu:
Klingt interessant. Und wie müsste man überwachen (auch wenn mir eigentlich der finale Wert auch reicht)? Müsste ich da sämtliche Funktionen verändern, weil jede kann ja irgendwas ändern? Ich hatte mal Enthought Traits gesehen. Ist das auch sowas?
Und was ungefähr würde mir inspect in diesem Zusammenhang leisten?
lunar

@Gerenuk: Nun, snafu meinte wohl, dass man das ".__dict__" jedes zu überwachenden Exemplars durch ein eigenen Mapping-Typen ersetzt, der Zugriffe loggt, so dass man die Veränderungen der Werte nachvollziehen kann. Davon rate ich nun noch nachdrücklicher ab, denn damit spielt man wirklich an den Interna von Objekten. Und sie funktioniert auch nicht für alle Typen, insbesondere nicht für builtins, Erweiterungstypen und Klassen mit ".__slots__". Außerdem enden bei weitem nicht alle Attributzugriffe im Namensraum eines Exemplars, die nennenswerte Ausnahme sind Deskriptoren.

Wenn überhaupt, dann bitte mit "sys.set_trace()" (ich habe das Gefühl, mich schon wieder zu wiederholen :roll:).

Für Enthought Traits, mit denen man sowas wohl möglicherweise umsetzen könnte, nach allem, was man der Dokumentation so entnehmen kann. Allerdings musst Du dafür jede zu überwachende Klasse entsprechend umschreiben. Deine Anforderung „von außen“ ist mithin nicht erfüllt.

Neidisch auf Deine „Lösung“ ist hier bestimmt niemand, weil jeder andere sich ungefähr vorstellen kann, wie Dein Quelltext wohl aussieht, wenn sowas nötig ist, und wohin das führen wird. Die Kritik ist ja kein Spaß, um Dich zu ärgern, sondern hat gute Gründe. Doch letztlich ist es selbstverständlich Dein Problem, wenn es Dich nicht interessiert, dass mehrere Personen nachdrücklich abraten. Niemand kann Dich letztlich daran hindern, zu tun, was Du nicht lassen kannst, also höre bitte auf zu jammern, dass sich niemand für Dein Problem interessiert und niemand Dir eine Lösung zeigen möchte.
Gerenuk
User
Beiträge: 69
Registriert: Donnerstag 21. Januar 2010, 22:27

@lunar:
Ach __dict__ umschreiben. Das ist natürlich eine interessante hackige Idee :)! Naja, ich brauche das nicht wirklich.

Neidisch auf die Lösung von Boaz (nicht meine)? Na was sonst? Ich meine er hat mir gleich die zwei Zeilen verraten anstatt in langen Paragraphen zu nörgeln wieso die Frage schonmal dumm ist um am Ende doch nur die Lösung nicht zu verraten. Ein kurzer Hinweis auf Risiken, *nachdem* man die Lösung verraten hat wäre vielleicht angemessen. Wenn ich frage wie man zum Bahnhof kommt, dann sagt ja auch keiner "Du hast zwar keine Details erläutert, aber der Bahnhof ist eh Mist und du solltest lieber ins Theater gehen. Und ich sag dir schonmal gar nicht wo der Bahnhof ist.". Du bist ja nicht meine Mutti die sich um meinen Quellcode sorgen muss. Wenn ich dir alles im Detail erklären würde, würdest du wahrscheinlich den Nutzen einsehen, aber es ist ja nicht zweckmäßig wenn ich mich erstmal für Frage rechtfertigen muss oder wieso ich überhaupt in Python programmiere oder usw... Diese Vorabdiskussion war ja nicht Thema.
Und irgendwelche Mutmaßungen über meinen Quellcode sind dann die Spitze der antwortvermeidenden Streiterei.

Naja, die Lösung habe ich zwar woanders gefunden, aber hier auch andere Ideen und Anregungen bekomme. Dann Danke erstmal!
BlackJack

@Gerenuk: Du hast nicht wie gefragt wie man zum Bahnhof kommt, sondern wie man sich in den Fuss schiesst. Auf Löcher im Fuss ist hier sicher keiner neidisch. ;-) Die Lösung von Boaz ist eben aus den Gründen die hier über die (nicht) zugesicherten Eigenschaften von `__del__()` mehrfach genannt wurden nicht wirklich zuverlässig. Das ersetzen des Typs und die reine Existenz einer `__del__()`-Methode können das Laufzeitverhalten der Anwendung stark verändern.
Antworten