Ich bekomme es ohne eval nicht hin
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Du zeichnest Dich immer mehr durch Sturheit und Aufstellen falscher Behauptungen aus. Dann probier doch einmal das:BlackJack hat geschrieben:Wenn ein Callback für ein Widget noch da ist, das Widget aber nicht mehr, dann gibt es keinen Crash. Schon wieder Unsinn, dessen Beweis Du wohl wieder schuldig bleiben wirst.
Code: Alles auswählen
from tkinter import Button
from tkinter import Tk
root = Tk()
# define a widget
mybutton1 = Button(text="Button 1")
mybutton1.pack()
mybutton2 = Button(text="Button 2")
mybutton2.pack()
# define a function, used as callback
def colorcallback(color):
mybutton1['bg'] = color
mybutton2['bg'] = color
# calling the callback
colorcallback('yellow')
# now dynamic, we destroy one button
mybutton2.destroy()
# And what would happen, if we would call the callback now?
colorcallback('yellow')
root.mainloop()
@Alfons Mittelmeyer: Das was Du da zeigst ist keine zyklische Referenz. Da ist am Ende einfach nur das selbe Objekt an drei verschiedene Namen gebunden und diese drei Bindungen sind völlig unabhängig voneinander. Zudem ist die letzte Zeile überflüssig darum würde man die nicht schreiben, denn `a` ist ja bereits an das selbe Objekt gebunden wie `c`, die letzte Zuweisung hat also keinen sinnvollen oder die Bindungen betreffenden sichtbaren Effekt. Das zeigt mal wieder Dein Unverständnis von dem was in der Dokumentation beschrieben ist und den Zusammenhängen bei der automatischen Speicherverwaltung.
Der Normalfall ist spezifiziert und zwar so dass von der Sprachspezifikation keinerlei Garantien gegeben werden wann und ob der Speicher von Objekten freigegeben wird. Wenn Du die Dokumentation anders verstehst, dann hast Du sie genau so wenig verstanden wie zyklische Referenzen zwischen Objekten. Die übrigens in der Praxis durchaus vorkommen, das ist bei weitem nichts exotisches, wie Du das anscheinend suggerieren möchtest wenn Du schreibst „Wer so etwas schreibt…” als wenn das nur Leute tun würden die nicht wissen was sie da tun.
Das Beispiel für den Crash ist Unsinn weil es sich bei dem Aufruf nicht um einen Rückruf (Callback) von der Schaltfläche handelt. Den kann es nicht mehr geben, weil es die Schaltfläche ja nicht mehr gibt. Zu dieser richtigen Behauptung stehe ich weiterhin (und meinetwegen auch stur), und würde gernen den Gegenbeweis sehen.
Der Normalfall ist spezifiziert und zwar so dass von der Sprachspezifikation keinerlei Garantien gegeben werden wann und ob der Speicher von Objekten freigegeben wird. Wenn Du die Dokumentation anders verstehst, dann hast Du sie genau so wenig verstanden wie zyklische Referenzen zwischen Objekten. Die übrigens in der Praxis durchaus vorkommen, das ist bei weitem nichts exotisches, wie Du das anscheinend suggerieren möchtest wenn Du schreibst „Wer so etwas schreibt…” als wenn das nur Leute tun würden die nicht wissen was sie da tun.
Das Beispiel für den Crash ist Unsinn weil es sich bei dem Aufruf nicht um einen Rückruf (Callback) von der Schaltfläche handelt. Den kann es nicht mehr geben, weil es die Schaltfläche ja nicht mehr gibt. Zu dieser richtigen Behauptung stehe ich weiterhin (und meinetwegen auch stur), und würde gernen den Gegenbeweis sehen.
Sowas wie hier macht niemand, weil es völlig sinnfrei ist, das ist aber auch nichtz zyklisches; zyklische Referenzen hast Du aber schon selbst gemacht, wie der Code in Deinem anderen Thread beweist. Übrigens in ObjectiveC sind zyklische Referenzen verboten und müssen vom Programmierer von Hand aufgelöst werden.Alfons Mittelmeyer hat geschrieben:Aber wer macht schon so etwas wie hier
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Wieso hast Du das geschrieben:DasIch hat geschrieben:eval() und exec leaken sehr leicht Referenzen.Dadurch lassen sich sehr leicht Memory Leaks produzierenCode: Alles auswählen
>>> eval(compile('foo = 1', '<string>', 'exec')) >>> foo 1
Wo wir übrigens bei Memory Leaks sind, folgendes als "foo.py" speichern:Code: Alles auswählen
class Foo(object): def __del__(self): print 'Collected' f = Foo()
So schnell haben wir ein Memory Leak mit exec was wir ohne gar nicht hätten.Code: Alles auswählen
λ python foo.py Collected λ python Python 2.7.10 (default, Jun 3 2015, 19:28:03) [GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.49)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import gc >>> for _ in range(10): ... execfile('foo.py', {}) ... print gc.collect(), len(gc.garbage) ... 9 1 9 2 9 3 9 4 9 5 9 6 9 7 9 8 9 9 9 10
Dazu kommen dann noch Probleme bei statischer Analyse, Debugging und Dokumentation die das Leben spannender machen. Die Kombination von Code der Introspektion betreibt und eval/exec dürfte auch zu Probleme führen. Von Performance dürfte man sich auch verabschieden können.
Wie kommt man eigentlich auf die Idee dass der Source Code in Form von Strings weniger Speicher verbraucht, als der equivalente Code in Form eines importiertem Moduls?
Code: Alles auswählen
# Hier legst Du mit dem zweiten Parameter {} neue Globals an und machst die alten unerreichbar. Und genau das ist das Problem.
execfile('foo.py', {})
# richtig ist:
execfile('foo.py')
- cofi
- Python-Forum Veteran
- Beiträge: 4432
- Registriert: Sonntag 30. März 2008, 04:16
- Wohnort: RGFybXN0YWR0
Das ist doch genau der Punkt: Es gibt Garbage, der nicht zugaenglich ist aber auch nicht abgeraeumt werden kann. Mit der "richtigen" Zeile hat man ein ganz anderes Thema, weil die Namen in den aktuellen Namensraum gebunden werden und eben noch zugaenglich sind und _deshalb_ nicht abgeraeumt werden koennen.Alfons Mittelmeyer hat geschrieben:Code: Alles auswählen
# Hier legst Du mit dem zweiten Parameter {} neue Globals an und machst die alten unerreichbar. Und genau das ist das Problem. execfile('foo.py', {}) # richtig ist: execfile('foo.py')
Michael Markert ❖ PEP 8 Übersetzung ❖ Tutorial Übersetzung (3.x) ⇒ Online-Version (Python 3.3) ❖ Deutscher Python-Insider ❖ Projekte
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Wohl nicht richtig gelesen: es geht nicht um zyklische sondern um zirkulare Referenzen:Sirius3 hat geschrieben:Sowas wie hier macht niemand, weil es völlig sinnfrei ist, das ist aber auch nichtz zyklisches; zyklische Referenzen hast Du aber schon selbst gemacht, wie der Code in Deinem anderen Thread beweist. Übrigens in ObjectiveC sind zyklische Referenzen verboten und müssen vom Programmierer von Hand aufgelöst werden.Alfons Mittelmeyer hat geschrieben:Aber wer macht schon so etwas wie hier
Quelle: https://docs.python.org/3/reference/datamodel.htmlNote del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x‘s reference count reaches zero. Some common situations that may prevent the reference count of an object from going to zero include: circular references between objects (e.g., a doubly-linked list or a tree data structure with parent and child pointers)
Mein Beispiel war natürlich wirklich etwas sinnfrei. Aber etwa in einer double linked list verweist ein Eintrag auf den nächsten und der wieder auf ihn zurück. Und so etwas ist nicht verboten, sondern so etwas macht man durchaus häufig. Aber ist klar, dass man so einen Eintrag nicht einfach mit pop() rausholt ohne sich um die Links zu kümmen. Man muss natürlich die Links des vorherigen und nachfolgenden Eintrages entsprechend anpassen und nicht einfach denken, dass Rauslöschen bereits genügt. Und wenn man ein Child löscht, sollte man es auch beim Parent austragen. Wenn man schlampt, muß man sich nicht wundern, wenn Objekte nicht in den Garbage wandern.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Leider falsch. Mit der richtigen Zeile ist es derselbe Namensraum und bei Neudefinition werden die alten Objekte automatisch abgeräumt, sofern es derselbe File mit denselben Definitionen war.cofi hat geschrieben:Das ist doch genau der Punkt: Es gibt Garbage, der nicht zugaenglich ist aber auch nicht abgeraeumt werden kann. Mit der "richtigen" Zeile hat man ein ganz anderes Thema, weil die Namen in den aktuellen Namensraum gebunden werden und eben noch zugaenglich sind und _deshalb_ nicht abgeraeumt werden koennen.Alfons Mittelmeyer hat geschrieben:Code: Alles auswählen
# Hier legst Du mit dem zweiten Parameter {} neue Globals an und machst die alten unerreichbar. Und genau das ist das Problem. execfile('foo.py', {}) # richtig ist: execfile('foo.py')
Bei einem anderern File mit anderen Namen, wachsen leider die Definitionen. Und darum geht es mir.
Zuletzt geändert von Alfons Mittelmeyer am Samstag 15. August 2015, 13:44, insgesamt 2-mal geändert.
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
Muss dann aber nicht besser werden... Will sagen: in Büchern zu Python steht auch viel mist...sparrow hat geschrieben:Das ist offensichtlich und diesen Umstand solltest du ändern.Alfons Mittelmeyer hat geschrieben:und habe auch noch kein Buch darüber gelesen.
- cofi
- Python-Forum Veteran
- Beiträge: 4432
- Registriert: Sonntag 30. März 2008, 04:16
- Wohnort: RGFybXN0YWR0
Nochmal: Das sind zwei verschiedene Probleme. Das gezeigte ist genau das was du nicht verstehen willst: Objekte werden nicht geloescht obwohl sie nicht erreichbar und damit einen Reference Count von 0 haben.
Deine Neukonstruktion ist das Ueberschreiben von Namen, damit sind die letzten 9 nicht mehr erreichbar und werde _moeglicherweise_ geloescht, aber das 10. nicht.
Diese 2 Situationen sind absolut nicht dasselbe Problem.
Deine Neukonstruktion ist das Ueberschreiben von Namen, damit sind die letzten 9 nicht mehr erreichbar und werde _moeglicherweise_ geloescht, aber das 10. nicht.
Diese 2 Situationen sind absolut nicht dasselbe Problem.
Michael Markert ❖ PEP 8 Übersetzung ❖ Tutorial Übersetzung (3.x) ⇒ Online-Version (Python 3.3) ❖ Deutscher Python-Insider ❖ Projekte
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Meine Neukonstruktion ist kein Neukonstruktion, sondern nur die richtige Konstruktion. Und richtig bemerkt, das zehnte wird nicht abgeräumt, erst bei Verlassen von Python. Und damit es auch abgeräumt wird, empfiehlt sich die Benutzung von del. Bei Benutzung von del ergibt die Benutzung von {} aber auch kein Memory Leak.cofi hat geschrieben:Nochmal: Das sind zwei verschiedene Probleme. Das gezeigte ist genau das was du nicht verstehen willst: Objekte werden nicht geloescht obwohl sie nicht erreichbar und damit einen Reference Count von 0 haben.
Deine Neukonstruktion ist das Ueberschreiben von Namen, damit sind die letzten 9 nicht mehr erreichbar und werde _moeglicherweise_ geloescht, aber das 10. nicht.
Diese 2 Situationen sind absolut nicht dasselbe Problem.
Und das Löschen ist auch nicht möglicherweise, sondern kann man an print('collected') sehen.
Zuletzt geändert von Alfons Mittelmeyer am Samstag 15. August 2015, 13:50, insgesamt 1-mal geändert.
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
vor allem steht da ja sicherlich wie in der Doku u.a. Dokumenten nicht alles drin, weil keiner außer dem OP wirklich Ahnung von Python hatjens hat geschrieben: Muss dann aber nicht besser werden... Will sagen: in Büchern zu Python steht auch viel mist...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Rechnest Du damit, dass andere vergessen haben worum es ging, weil Du mir schon Unsinn nachsagst. Ich hatte doch geschrieben:BlackJack hat geschrieben:Das Beispiel für den Crash ist Unsinn weil es sich bei dem Aufruf nicht um einen Rückruf (Callback) von der Schaltfläche handelt. Den kann es nicht mehr geben, weil es die Schaltfläche ja nicht mehr gibt. Zu dieser richtigen Behauptung stehe ich weiterhin (und meinetwegen auch stur), und würde gernen den Gegenbeweis sehen.
Also wenn man nicht tkinter callbacks verwendet, wie Rückruf von der Schaltfläche, sondern eigene Callbacks, dann muss man sich um Lebenszeiten von Referenzen kümmern, ansonsten gibt es einen Crash. Und Du schriebsts das wäre Unsinn. Und genauso geschah der Crash. Und jetzt schreibst das Beispiel wäre Unsinn, weil es kein tkinter Callback gewesen wäre. Also bitte lies erst einmal genau, was ich geschrieben hatte, bevor Du mich Unsinn zu schreiben bezichtigst.Wenn Ihr allerdings während der Laufzeit Widgets löscht und nicht nur tkinter callbacks einsetzt sondern auch eigene - ich hatte message Callbacks implementiert - dann habt Ihr dafür zu sorgen, dass diese Referenzen mit Löschen des zugeordneten Widgets verschwinden, denn sonst habt Ihr einen Crash, wenn der Callback für die Widgets noch da ist, die Widgets allerdings nicht mehr. Del braucht es nicht immer, denn wenn die Referenz in einem dictionary oder einer Liste steht, genügt auch ein pop().
Und meine Lösung für einen solchen Fall ist ein Messagecallback, für welchen man einzeln die Buttons registriert. Und wenn man einen Button mit destroy löscht, dann hat dieser in seiner destroy Methode, dass auch alle Message Callbacks für die er eingetragen ist, deregistriert werden. Außerdem sind Messages zu empfehlen, wenn sich Objekte gar nicht im Programm befinden, sondern auf einem anderen Computer in Amerika. Da kommt man mit einem Methodenaufruf nicht sehr weit.
@Alfons Mittelmeyer: Doch ich habe richtig gelesen und das ich ein Synonym, das in der Graphentheorie geräuchlicher ist, statt der wortwörtlichen Übersetzung des Wortes „circular” verwendet habe, zeigt das ich den Inhalt der Dokumentation verstanden habe und ihn nicht nur 1:1 wiedergeben kann, sondern auch sinnerhaltend mit anderen Worten. Was normalerweise ein gutes Zeichen ist um zu sehen ob jemand etwas wirklich verstanden hat, oder nur etwas vorgebenes aufsagt ohne es zwingend verstanden zu haben.
Natürlich wird man bei einer `pop()`-Implementierung einer doppelt verketteten Liste den Zyklus in der Referenzierung aufbrechen *müssen*. Es geht um so etwas:
Hierbei würden mindestens zwei Zyklen erhalten bleiben und damit würde das ``del`` auch bei CPython keine sofortige Freigabe des Speichers garantieren. Und nochmal: Ganz allgemein garantiert die Sprachspezifikation weder wann noch ob Speicher von nicht mehr erreichbaren Objekten freigegeben wird. CPython (aktuelle Implementierung(en)) dagegen *garantiert* sogar das die Knoten-Objekte von so einer doppelt verketteten Liste *nie* freigegeben werden wenn sie eine eigene `__del__()`-Methode implementieren!
Natürlich wird man bei einer `pop()`-Implementierung einer doppelt verketteten Liste den Zyklus in der Referenzierung aufbrechen *müssen*. Es geht um so etwas:
Code: Alles auswählen
doubly_linked_list = LinkedList([23, 42, 4711])
del doubly_linked_list
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Ja, da hast Du vollkomen recht, es ist nicht exakt dasselbe Problem. Ich habe noch einmal darüber nachgedacht und finde diese Zeile besonders interessant:cofi hat geschrieben:Nochmal: Das sind zwei verschiedene Probleme. Das gezeigte ist genau das was du nicht verstehen willst: Objekte werden nicht geloescht obwohl sie nicht erreichbar und damit einen Reference Count von 0 haben.
Deine Neukonstruktion ist das Ueberschreiben von Namen, damit sind die letzten 9 nicht mehr erreichbar und werde _moeglicherweise_ geloescht, aber das 10. nicht.
Diese 2 Situationen sind absolut nicht dasselbe Problem.
Code: Alles auswählen
execfile('foo.py', {})
Richtig formuliert ist diese Zeile höchstwahrscheinlich die Lösung aller Memory Leak Probleme, die mit Laden und Ausführen von Scripten in nicht limitierter Anzahl auftreten. Ich habe es noch nicht ausprobiert, aber ich denke, dass es so klappen könnte:
Code: Alles auswählen
a = {}
execfile('foo.py',a)
- jens
- Python-Forum Veteran
- Beiträge: 8502
- Registriert: Dienstag 10. August 2004, 09:40
- Wohnort: duisburg
- Kontaktdaten:
jens hat geschrieben:Es steht noch nach wie vor aus, beweise dafür zu zeigen, das es überhaupt ein Problem gibt
Alfons Mittelmeyer hat geschrieben:die Lösung aller Memory Leak Probleme, die mit Laden und Ausführen von Scripten in nicht limitierter Anzahl auftreten. Ich habe es noch nicht ausprobiert
jens hat geschrieben:Mal abgesehen davon ob nun ein memoryleak existiert oder nicht:
Wie lange arbeitet man wohl mit einem GUI Designer am Stück?!? Wie viel Elemente erstellt man und verwirft sie wieder?
- cofi
- Python-Forum Veteran
- Beiträge: 4432
- Registriert: Sonntag 30. März 2008, 04:16
- Wohnort: RGFybXN0YWR0
Damit hast du nun den Namen `a` der die Referenz auf den Namensraum haelt. Der war davor nicht da, der Referenzzaehler des Namensraumes (und transitiv dann auch aller Objekte, die nur dort existiert haben) war davor auf 0 und _trotzdem_ wurde er nicht entfernt.
Solltest du jetzt also meinen, `del a` hilft gegen das Problem: Tut es nicht.
Solltest du jetzt also meinen, `del a` hilft gegen das Problem: Tut es nicht.
Michael Markert ❖ PEP 8 Übersetzung ❖ Tutorial Übersetzung (3.x) ⇒ Online-Version (Python 3.3) ❖ Deutscher Python-Insider ❖ Projekte