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.
Gerenuk
User
Beiträge: 69
Registriert: Donnerstag 21. Januar 2010, 22:27

Zum Debuggen würde ich gerne, dass in normalen Programläufen einige Variablen in Files gesichert werden.

Ich stelle es mir als Aufruf so vor

Code: Alles auswählen

x=...
store_var(x, "x.dat")
...
und nachdem das Programm gelaufen ist würde ich gerne den letzten Wert von x in x.dat nachlesen (ist ein pickle dump oder vielleicht lesbareres YAML besser?).

Dazu würde man vermutlich das Löschen umbiegen also irgendwie die __del__ Methode durch store_var verändern lassen?! Bloß irgendwie kriege ich das nicht gebacken :(
Weiß jemand wie das gehen würde? Funktioniert es dann auch für Objekte wie int?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Gerenuk hat geschrieben:Dazu würde man vermutlich das Löschen umbiegen also irgendwie die __del__ Methode durch store_var verändern lassen?! Bloß irgendwie kriege ich das nicht gebacken :(
Weiß jemand wie das gehen würde? Funktioniert es dann auch für Objekte wie int?
Nun das geht, Stichwort `monkey patching`. Aber built-ins wie `int` bekommst du damit nicht.
Daneben ist es ziemlich unschoen IO im "Destruktor" zu machen -- als ob "normaler" Code im "Destruktor" nich schon schlimm genug waere.

Von dem Weg wuerd ich dir abraten, überleg dir lieber ein sinnvolles Logging-Konzept; v.a. da post-mortem Analyse dann evtl sinnvoller vonstatten geht, da man den Fehlerweg evtl nachvollziehen kann.
Gerenuk
User
Beiträge: 69
Registriert: Donnerstag 21. Januar 2010, 22:27

Und wie würde man das einpatchen? Es scheint auf dem naheliegendsten Weg komme ich an __del__ nachträglich nicht ran :(
Natürlich gefällt ein Designkonzept nicht immer jedem, aber die Frage ist ja wie es geht ungeachtet der eigenen Philosophie :)

Ich habe halt ein normales Programm und will da nicht dieses Debugzeug tief reincodieren sondern es eher beliebig von außen dranspielen. Klar könnte ich print statements einbauen, aber das ist hässlich, weil ich dann immer suchen muss, wo denn der finale Zustand des Objektes ist. Und post-mortem ist das auch nicht, weil es mir um gewöhnliche Programmabläufe geht, wo ich eher logische Fehler in den Daten habe.
lunar

@Gerenuk: Dieses „Designkonzept“ wird fast überhaupt niemandem gefallen, der einigermaßen vernünftig denkt. Schon das Wort „Designkonzept“ ist eigentlich zu viel des Guten für diese Idee. Nichts für ungut :)

Ich schließe mich cofi an, und rate lieber zu umfassendem und umfangreichem Logging, und zwar nicht mit "print", sondern mit "logging" aus der Standardbibliothek (oder mit dem Drittmodul logbook, wenn man es moderner mag). Das ist kein „Debugzeug“, sondern ein üblicher und sauberer Weg, den Zustand eines Programms nachzuverfolgen. Jeder Dienst, der auf Deinem System so läuft, benutzt das, um über seinen Zustand zu informieren (unter Linux mittels syslog nach "/var/log/", unter Windows ins Ereignislog, das über die Systemverwaltung erreichbar ist). Was für Dienste seit Jahrzehnten funktioniert, kann für Dich so schlecht nicht sein. ;)

Im Gegensatz zu Deiner Idee ist dieser Ansatz sauber, weil das Logging explizit ohne Magie geschieht, und flexibler, weil man das Logging von außen konfigurieren und somit beispielsweise die Debugging-Ausgaben im Produktivbetrieb abschalten kann, ohne den Quelltext verändern zu müssen.

Dein Vorhaben dagegen finde ich ziemlich bedenklich. Ich halte die Gefahr für groß, dass Du mit solcher Magie, mit solch tiefen Eingriffen in die Interna von Objekten und des Interpreters überhaupt erst neue Fehler einführst, die nicht mehr einfach zu debuggen sind, eben weil sie in den Interna von Objekten geschehen. In ".__del__()" können beispielsweise bestimmte Namen gar nicht mehr existieren, was dann zu Ausnahmen bei Erzeugen der Debugging-Ausgabe führen kann. Damit erreichst Du also mehr das Gegenteil dessen, was Du erreichen willst: Statt die Fehlersuche zu erleichtern, führst Du neue, noch schwerer zu findenden Fehler ein.

Außerdem ist der Ansatz, „einige Variablen“ zu sichern, sowieso von vorne herein zu scheitern verurteilt, weil Du gemäß Murphys Law eben genau die Variable, die für die Analyse des gerade aufgetretenen Fehlers entscheidend wäre, nicht gesichert hast ;)

Im Allgemeinen ist die Idee eh nicht umsetzbar. Man kann nicht einfach jedes existierende Objekt nachträglich verändern. Du musst jedwede Klasse, die Du verwendest, mit einer eigenen ".__del__()" Implementierung patchen (auf Exemplaren geht das nicht), und selbst wenn Du dabei keine Klasse vergessen hast, bleibt immer noch das Problem, dass ".__del__()" nicht zuverlässig aufgerufen wird.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Gerenuk hat geschrieben:Und wie würde man das einpatchen? Es scheint auf dem naheliegendsten Weg komme ich an __del__ nachträglich nicht ran :(
Sicher:

Code: Alles auswählen

In [1]: class Klass(object):
   ...:     pass

In [2]: def print_(self): print "hello there"

In [3]: a = Klass()

In [4]: Klass.f = print_

In [4]: a.f()
hello there
Zumindet kann ich mir keinen einfacheren Weg vorstellen, aber du handelst dir unglaublich viele Probleme ein, wie lunar schon ausgefuehrt hat.
Gerenuk
User
Beiträge: 69
Registriert: Donnerstag 21. Januar 2010, 22:27

Hmm, also ich finde da ziemlich unproduktiv, wenn man zwar keine Antwort gibt aber dafür erstmal langwierig die Frage in Frage stellt. Es soll mal meine Sorge sein ob ich die richtigen Variablen auswähle, denn schließlich verantworte ich selbst diesen Code. Ich habe mir das mit meiner speziellen Anforderung überlegt und es wäre zu komplex jetzt erst alle Details erläutern zu müssen nur um mich zu rechtfertigen und auch zu erklären wieso logging (was ich sonst viel nutze) hier nicht praktisch ist. Die Alternativen kenne ich gut, aber trotzdem Danke für die Zusammenfassung.

Also, möchte mir jemand beim Monkey-Patching helfen oder zumindest Ansätze zeigen?
Zuletzt geändert von Gerenuk am Sonntag 15. Mai 2011, 18:49, insgesamt 1-mal geändert.
Gerenuk
User
Beiträge: 69
Registriert: Donnerstag 21. Januar 2010, 22:27

EDIT:

Jetzt geht es doch. Anscheinend ist __del__ eine Klassenfunktion

Code: Alles auswählen

class Test(object):
    def __del__(self):
        print("Old del")

def p(self):
    print("New del")

a=Test()
Test.__del__=p
del a
Hat jemand eine Idee wie man store_var umsetzen könnte? Hmm, ich finde das verwirrend ... aber notwendig ;)
Und hmm, was ist wenn ich nur einzelne Objekte speichern will? Dann müsste ich die Klasse dynamisch ändern?! Huch, wie geht das denn?
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Gerenuk hat geschrieben:Anscheinend ist __del__ eine Klassenfunktion
__del__ ist keine Klassenfunktion, sondern eine sog. special method. In der Doku zu special methods steht "For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary".
Gerenuk hat geschrieben:Hat jemand eine Idee wie man store_var umsetzen könnte? Hmm, ich finde das verwirrend ... aber notwendig ;) Und hmm, was ist wenn ich nur einzelne Objekte speichern will? Dann müsste ich die Klasse dynamisch ändern?! Huch, wie geht das denn?
Das, was du vorhast, wird nicht funktionieren. In der Doku zu object.__del__() steht "Called when the instance is about to be destroyed. [...] It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits." oder auf deutsch: wenn ein Objekt zerstört wird, wird __del__ aufgerufen, außer es wird nicht aufgerufen. IAW, selbst, wenn es dir gelingen sollte, store_var() und __del__-Methoden für alle relevanten Objekte so zu schreiben, dass keine weiteren Fehler oder desaströsen Nebeneffekte auftreten, dann ist immer noch nicht garantiert, dass in x.dat der letzte Wert steht, den x hatte. Beispiel:

Code: Alles auswählen

import sys
from super_module_that_provides_the_store_var_magic import store_var, write_magically_to_file

class MyObjectWithDel(object):
    def __init__(self, number):
        self.number = number
    def __del__(self):
        write_magically_to_file(key=self, value=self.number)

x = MyObjectWithDel(1) # einmal
store_var(x, 'x.dat')  # registrieren zum späteren magischen speichern

x = MyObjectWithDel(2) # und nochmal
store_var(x, 'x.dat')  # registrieren zum späteren magischen speichern

sys.exit(0) # hm... was steht jetzt in x.dat? Laut Doku entweder 2, oder 1, oder gar nichts...
Im Übrigen ist es sogar möglich, dass beide Objekte rechtzeitig gelöscht und ihre Werte nach x.dat geschrieben wurden, aber so, dass am Ende dort der Wert 1 steht. Warum, fragst du? Weil der garbage collector nicht gezwungen ist, irgendeine deterministische Reihenfolge beim Löschen von Objekten einzuhalten. Und sollte folgendes irgendwo in deinem code auftauchen:

Code: Alles auswählen

import gc
gc.disable()
dann wird niemals irgendeine __del__-Methode aufgerufen. Und wenn es komplizierte zirkuläre Referenzen zwischen Objekten gibt, werden diese, selbst bei angeschaltetem garbage collector, niemals gelöscht, sondern werden zum memory leak. Auch da wird dann __del__() nie aufgerufen.

Deswegen empfehle ich, im Anschluss an cofi und lunar, es mal mit logging zu versuchen.

Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.
lunar

Wie Du "store_var" umsetzen kannst, musst Du schon selbst wissen, nur Du weißt schließlich, was genau Du wohin speichern möchtest. Da Du das auch nicht verraten hast, könnten wir nur raten, und das wäre dann wirklich unproduktiv.

Die Warnung, dass ".__del__()" nicht aufgerufen wird, ist im Übrigen auch ernst zu nehmen, gerade bei Deinem Vorhaben. Beendet sich der Interpreter aufgrund einer Ausnahme, wie es ja im Fehlerfall wahrscheinlich ist, so sind unter Umständen noch ziemlich viele Objekte referenziert, da der Traceback der Ausnahme Referenzen auch lokale Namen in den betroffenen Funktionen halten kann. Im Zweifelsfall wird ".__del__()" dann für genau die Objekte, an denen Du interessiert bist, weil sie den Fehler verursacht haben, nicht aufgerufen.

Eine andere Möglichkeit wäre "sys.set_trace()". Dort könntest Du beispielsweise beim Verlassen jeder Funktion alle lokalen Namen sichern. Der Preis ist, dass das Programm wesentlich langsamer läuft. Außerdem arbeitest Du dann immer noch in den Interna, was wiederum neue, schwer zu findende Fehler verursachen kann.

Und das alles wird man Dir, so hoffe ich doch, auch auf StackOverflow sagen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Gerenuk hat geschrieben:Hat jemand eine Idee wie man store_var umsetzen könnte? Hmm, ich finde das verwirrend ... aber notwendig ;)
Na, einfach mit ``pickle.dump``. Mehr als Dokumentation lesen und verstehen ist das nicht.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Gerenuk
User
Beiträge: 69
Registriert: Donnerstag 21. Januar 2010, 22:27

pillmuncher hat geschrieben:Das, was du vorhast, wird nicht funktionieren. In der Doku zu object.__del__() steht "Called when the instance is about to be destroyed. [...] It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits." oder auf deutsch: wenn ein Objekt zerstört wird, wird __del__ aufgerufen, außer es wird nicht aufgerufen.
Na mit etwas mehr Kreativität und weniger Schwarzmalerei geht das doch bestimmt?! Ich meine du nennst den GC ja selbst und bestimmt hat der manuelle Collect?
Wieso wird versucht es hier kaputt zureden, anstatt einfach zu sagen wie man die Klasse von einem existierenden Objekt ändern kann, so dass sie eine neue __del__ Methode hat? Entweder man weiß es oder eben nicht. Ein "Ich weiß es nicht, aber das ist eh scheiße" bringt mich nicht weiter.

Zum Glück hat Boaz auf Stackoverflow die Frage verstanden. Bleibt nur die Frage ob man das noch abrunden kann, damit alles passt. Aber so bin ich schonmal weiter.
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Gerenuk: Ich habe nicht geschrieben, dass ich nicht weiß, wie man __del__ an ein Objekt, im Gegensatz zu dessen Klasse, binden kann, so dass der garbage collector das mitbekommt. Statt dessen habe ich geschrieben, dass ich weiß, dass es nicht geht. Nur nicht so explizit, sondern in Form zweier Links auf die Dokumentation, wo genau steht, wie special methods implizit aufgerufen werden, und dass ein Aufruf von __del__ niemals garantiert ist. Das hat nichts mit Schwarzseherei zu tun, sondern mit schlichter Rationalität. Und was Kreativität anbelangt, da schlage ich vor, diese darauf zu verwenden, eine Lösung zu erfinden, die funktioniert, und nicht eine, die mehr Probleme mit sich bringt, als sie löst.

Um Jamie Zawinski zu paraphrasieren: Some people, when confronted with a problem, think "I know, I'll use object.__del__()." Now they have two problems.
In specifications, Murphy's Law supersedes Ohm's.
Gerenuk
User
Beiträge: 69
Registriert: Donnerstag 21. Januar 2010, 22:27

pillmuncher hat geschrieben: Das hat nichts mit Schwarzseherei zu tun, sondern mit schlichter Rationalität. Und was Kreativität anbelangt, da schlage ich vor, diese darauf zu verwenden, eine Lösung zu erfinden, die funktioniert, und nicht eine, die mehr Probleme mit sich bringt, als sie löst.
Darauf habe ich doch gerade geantwortet. Kann man am Ende des Programmes den GC explizit anweisen alles zu löschen oder nicht? Soll ich selbst nachschauen? Ich vermute es geht und es wäre nur ein Befehl. Das meine ich mit Schwarzmalerei.
Und selbst im schlimmsten Fall sind ein paar unvollständige Debug Infos besser als gar keine.
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Gerenuk hat geschrieben:Kann man am Ende des Programmes den GC explizit anweisen alles zu löschen oder nicht? Soll ich selbst nachschauen?
Du hast recht, das wäre geradezu eine Zumutung.
Gerenuk hat geschrieben:Ich vermute es geht und es wäre nur ein Befehl. Das meine ich mit Schwarzmalerei.
Und selbst im schlimmsten Fall sind ein paar unvollständige Debug Infos besser als gar keine.
Klar kannst du das selber aufrufen. Aber auch dann ist nicht garantiert, dass der gc alles aufräumt. Was, wenn dein Programm vorher abschmiert? oder wenn es zirkuläre Referenzen gibt? Und wie ich schon geschrieben habe, werden die Werte nicht in der selben Reihenfolge gespeichert, wie die Variablen, deren Werte sie sind, out of scope gehen, sondern der gc löscht die Objekte in der Reihenfolge, die ihm gerade in den Kram passt. Nochmal:

Code: Alles auswählen

x = MyObjectWithDel(1)
x = MyObjectWithDel(2)
x = MyObjectWithDel(3)
del x
gc.collect()
Selbst wenn wir davon ausgehen, dass der gc alle drei Objekte löscht und ordentlich __del__() auf ihnen aufruft, er wird sie in irgendeiner Reihenfolge löschen, zB. 2, 3, 1, und dann steht in deiner Debug Datei nicht ein unvollständiger Wert, sondern ein falscher. Viel Spass beim Debuggen der Debug Infos.

Des Weiteren: Du scheinst zu glauben, dass del x das Objekt löscht, oder als gelöscht markiert. Das tut es natürlich nicht. Es löscht nur den Namen 'x' aus dem Namespace und dekrementiert den ref counter des Objekts. Angenommen folgender Code:

Code: Alles auswählen

x = MyObjectWithDel(1)
some_object.some_method(x)
del x
Für's folgende: Sei V eine Variable, dann sei |V| das Objekt, auf das V verweist.

Dann kann es sein, dass |some_object| noch eine Referenz auf das ursprüngliche Objekt |x| hält, und dann wird |x| erst gelöscht werden, nachdem |some_object| gelöscht wurde. Natürlich vorausgesetzt, dass |some_object| nicht auch wieder von irgendwo her referenziert wird, etc. pp. ad nauseam. Am Ende wirst du dann jede Variable in deinem Programm händisch del'en müssen, und voila, willkommen in der C++-Welt. Python ist nicht C++, und vieles, was in C++ sinnvoll oder sogar notwendig ist, ist in Python unnötig, unsinnig und schädlich.

Und jetzt komm nicht wieder mit Schwarzseherei. Wenn Sachen nicht funktionieren, dann wird keine rosa Brille sie zum funktionieren bringen. Das war früher anders, als das Wünschen noch geholfen hat.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Zum Spaß:

Code: Alles auswählen

>>> class T(object):
...     def __init__(self, v):
...         self.v = v
...     def __del__(self):
...         print self.v
...
>>> m = map(T, range(3))
>>> for i in m:
...     pass
...
>>> del m
1
0
>>> del i
2
>>> m = map(T, range(3))
>>> for i in m:
...     pass
...
>>> del i
>>> del m
2
1
0
>>> import gc
>>> x = T(None)
>>> y = T(x)
>>> x.v = y
>>> del x
>>> del y
>>> gc.collect()
0
>>> gc.garbage
[]
>>> z = T('reachable')
>>> [o for o in gc.get_objects() if isinstance(o, T)]
[<__main__.T object at 0x7ff4a08c>, <__main__.T object at 0x7ff4a1cc>, <__main__.T object at 0x7ff4a38c>]
In der Doku zu gc.collect() steht: With no arguments, run a full collection. [...] The number of unreachable objects found is returned.
In der Doku zu gc.garbage steht: A list of objects which the collector found to be unreachable but could not be freed (uncollectable objects). By default, this list contains only objects with __del__() methods.
In der Doku zu gc.get_objects() steht: Returns a list of all objects tracked by the collector

Zwei der Objekte, die von gc.get_objects() zurückgegeben werden, sind also unreachable, werden aber vom garbage collector als solche nicht erkannt. Interessant.
In specifications, Murphy's Law supersedes Ohm's.
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.
Antworten