Ich bekomme es ohne eval nicht hin

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
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Es steht noch nach wie vor aus, beweise dafür zu zeigen, das es überhaupt ein Problem gibt :P

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

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.
Du zeichnest Dich immer mehr durch Sturheit und Aufstellen falscher Behauptungen aus. Dann probier doch einmal das:

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()
BlackJack

@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.
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

Alfons Mittelmeyer hat geschrieben:Aber wer macht schon so etwas wie hier
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.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Alfons Mittelmeyer hat geschrieben:und habe auch noch kein Buch darüber gelesen.
Das ist offensichtlich und diesen Umstand solltest du ändern.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

DasIch hat geschrieben:eval() und exec leaken sehr leicht Referenzen.

Code: Alles auswählen

>>> eval(compile('foo = 1', '<string>', 'exec'))
>>> foo
1
Dadurch lassen sich sehr leicht Memory Leaks produzieren

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()

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
So schnell haben wir ein Memory Leak mit exec was wir ohne gar nicht hätten.

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?
Wieso hast Du das 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')
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

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')
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
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:
Alfons Mittelmeyer hat geschrieben:Aber wer macht schon so etwas wie hier
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.
Wohl nicht richtig gelesen: es geht nicht um zyklische sondern um zirkulare Referenzen:
Note 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)
Quelle: https://docs.python.org/3/reference/datamodel.html
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.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

cofi hat geschrieben:
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')
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.
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.

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.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

sparrow hat geschrieben:
Alfons Mittelmeyer hat geschrieben:und habe auch noch kein Buch darüber gelesen.
Das ist offensichtlich und diesen Umstand solltest du ändern.
Muss dann aber nicht besser werden... Will sagen: in Büchern zu Python steht auch viel mist...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
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.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

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.
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.

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.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

jens hat geschrieben: Muss dann aber nicht besser werden... Will sagen: in Büchern zu Python steht auch viel mist...
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 hat :twisted:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das lustige hier ist,das wieder nach Problemlösungen gesucht wird,die praktisch nicht existieren...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

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.
Rechnest Du damit, dass andere vergessen haben worum es ging, weil Du mir schon Unsinn nachsagst. Ich hatte doch geschrieben:
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().
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.

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.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@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:

Code: Alles auswählen

doubly_linked_list = LinkedList([23, 42, 4711])
del doubly_linked_list
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!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

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.
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:

Code: Alles auswählen

execfile('foo.py', {})
Es ist eine sehr gute Idee, einen neuen unabhängigen Namensraum zu verwenden. So brauche ich etwa beim GUI Designer keinerlei Rücksicht nehmen, welche Funktionen die Anwendung haben könnte, sofern der GUI Designer zu einer bereits existierenden Anwendung dazu exportiert wird. Und kann meine Implementation in beliebiger Form gestalten. Diese Zeile enthält nur einen klitzeklein aussehenden Fehler, der aber verhindert, dass die Speicherverwaltung von Python greifen kann.

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)
Benutzeravatar
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 :P
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?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
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.
Gesperrt