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.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

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

DasIch hat geschrieben:eval() und exec leaken sehr leicht Referenzen.
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
Das schreibe ich ja die ganze Zeit, ich habe das selber getestet und wenn man del verwendet, gibt es kein Memory Leak, also del F und del Foo fehlen hier.

Und über del habe ich ausser Vermutungen und uninteressante Fälle fast nichts im Internet gefunden. Aber hier ist es einmal richtig beschrieben, wenn Ihr mir schon nicht glauben wollt, dann ist hier noch eine Quelle:
The reason del foo doesn't work is also simple to explain. I initially confused myself by thinking that del foo would call the destructor, but it only decreases the reference counter on the object (and removes the reference from the local scope). Since the count in the code above for Foo and Bar is 2 (one in the main program, one in the other instances), the count will only go down to 1 for the object
Quelle: http://www.electricmonk.nl/log/2008/07/ ... ion-notes/

Also Referenzzähler eins runter und Namen aus Namespace entfernen. Und damit erklärt man ein Objekt, auf das nur der Name verweist und nichts anderes als Waste.

Ach sorry, das mit dem Beweis hatte ich im tkinter Forum beschrieben und nicht hier. Hatte ein Testprogramm mit Funktionen dynamisch erzeugen und ausführen. Ohne del Crash nach ein paar Minuten mit del ohne Probleme auch am nächsten Morgen.

Und Anwendungsfälle gibt es genug. Hier handelt es sich um das Top-level script environment:
'__main__' is the name of the scope in which top-level code executes. A module’s __name__ is set equal to '__main__' when read from standard input, a script, or from an interactive prompt
Quelle: https://docs.python.org/3/library/__main__.html

Und viele starten nicht immer python neu, sondern beschicken python über den standardinput mit unterschiedlichsten Scripten. Und auch da kann es bei unterschiedlichsten Scripten und längerer Laufzeit dann zu Memoryleaks und Crashs kommen, wenn man den globalen Müll der Scripte nicht mit del beseitigt.
Sirius3
User
Beiträge: 18294
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: das ist Dein Problem, dass Du irgendwelchen dubiosen Quellen aus dem Internet vertraust. Die wirklich verlässliche Quelle ist die Python-Dokumentation:
The Python Language Reference hat geschrieben:Deletion of a name removes the binding of that name from the local or global namespace
Hier ist nicht von Referenzen die Rede. Hier geht es nur um Namen. Ist der entsprechende Name noch im Namespace oder nicht. "del" hat also erst einmal nichts mit Speichermanagement zu tun.
Benutzeravatar
pillmuncher
User
Beiträge: 1531
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Alfons Mittelmeyer hat geschrieben:
pillmuncher hat geschrieben:
Alfons Mittelmeyer hat geschrieben:Also was ist der Zweck von del.
Insbesondere die letzten beiden Zeilen sollten klarmachen, dass del zur manuellen Speicherverwaltung völlig ungeeignet ist.
Ja toll, del soll ganz am Ende gemacht werden, wo definitiv das Programm zuende ist, und nicht mittendrin, wenn man die Variablen noch braucht.
Nein, Du hast es immer noch nicht begriffen.

del löscht keine Objekte aus dem Speicher, sondern macht eines von zwei Dingen.

Erstens: bei einer Collection, die den Subscriptions-Operator (d.h. z.B. foo[bar]) implementiert, kann man mit del Elemente aus der Collection entfernen. Ob das entfernte Objekt dann vom Garbage Collector aufgeräumt werden kann, hängt davon ab, ob es keine anderen Verweise auf dieses Objekt gibt.

Zweitens: in allen anderen Fällen löscht man mit del einen Namen aus einem Namespace, wodurch natürlich die Assoziation des Names mit dem Objekt, auf das er verweist, ebenfalls gelöst wird. Nun können viele Namen gleichzeitig auf dasselbe Objekt verweisen und ein Objekt wird frühestens vom Garbage Collector aufgeräumt, wenn keine Verweise (durch Namen, aber auch namelose Verweise aus Collections heraus) darauf mehr existieren. Auch dann ist aber nicht garantiert, dass ein verwaistes Objekt jemals aufgeräumt wird, zB. gibt es (seltene) Fälle, wo das Vorhandensein einer __del__() Methode den GC beim Aufräumen behindert.

So, wie ich es sehe, möchtest du einfach nicht in einer Sprache mit Garbage Collector arbeiten, sondern Speicher lieber manuell verwalten. Dann ist Python die falsche Sprache.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Alfons Mittelmeyer hat geschrieben: Und viele starten nicht immer python neu, sondern beschicken python über den standardinput mit unterschiedlichsten Scripten.
Ach ja?
Welche Beispiele kannst du da nennen?
Und in welche hilft del?

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

Sirius3 hat geschrieben:@Alfons Mittelmeyer: das ist Dein Problem, dass Du irgendwelchen dubiosen Quellen aus dem Internet vertraust. Die wirklich verlässliche Quelle ist die Python-Dokumentation:
The Python Language Reference hat geschrieben:Deletion of a name removes the binding of that name from the local or global namespace
Hier ist nicht von Referenzen die Rede. Hier geht es nur um Namen. Ist der entsprechende Name noch im Namespace oder nicht. "del" hat also erst einmal nichts mit Speichermanagement zu tun.
Ja denkst Du dass Dokumentationen so etwas wie die Bibel sind? Entweder die Autoren wissen es selber nicht genau oder sie vergessen das auch zu schreiben oder sie geben sich nicht die Mühe auch die Details zu beschreiben.
Das ist eben so. Was in Dokumentationen steht, stimmt. Aber manchmal steht eben nur die Hälfte drin. Daher kann man keinerlei Dokumentationen ernstlich als verläßliche Quellen für das was nicht drin steht, gebrauchen.

Und das ist eben ein Fakt, der nicht in der Doku steht:
decreases the reference counter on the object
Und da hilft keine Diskussion, sondern selber testen. Oder ihr fragt die Python Core Entwickler

Und warum, weiß ich dass man das so implementiert? Weil es etwa auch in .Net so implementiert ist, und weil ich das genauso implementiert habe. Und alles andere Unsinn ist.

Also wenn man ein Objekt definiert, dann geht der Referenzähler zuerst mal auf eins. Wenn man ein globales Objekt anschließend mit del löscht, dann soll es aus dem Namensraum beseitigt werden aber der Referenzzähler auf eins bleiben? Sozusagen zugriffsloser dauerhafter Müll? Wie kann man nur so einen Blödsinn glauben?
Sirius3
User
Beiträge: 18294
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: In Python ist die Speicherverwaltung nicht spezifiziert. Also kann man auch keine Aussagen darüber treffen. An die Spezifikation muß sich jede Python-Implementierung halten, was nicht drin steht, darauf kann sich kein Programmierer verlassen.

Und Dein unsägliches Crash-Beispiel, ohne del zu benutzen:

Code: Alles auswählen

from itertools import count

for i in count():
    namespace = {}
    func_str = ("def function{0}(): print 'This is function {0}'\n"
                "function{0}()").format(i)
    exec(func_str, namespace)
Was aber nicht bedeuten soll, dass man so ein Konstrukt einsetzen sollte.
BlackJack

@Alfons Mittelmeyer: ``del`` ganz am Ende macht keinen Sinn weil dann ja das Programm zuende ist und sowieso alles abgeräumt wird. Wenn man beliebige Python-Anwendungen aus einer Python-Anwendung heraus starten will, dann nimmt man dafür Prozesse. Und schon hat man kein Problem mehr das man die Anwendungen irgendwie speziell schreiben muss, damit am Ende davon nichts im Speicher bleibt. Das ist eine ganz einfache Lösung die auch tatsächlich funktioniert, egal welche Sonderfälle es in der Python-Implementierung bei Garbage gibt und egal welche Methode für die Speicherbereinigung tatsächlich benutzt wird.

Wer beschickt Python über stdin mit unterschiedlichen Skripten? Beispiele? Also Mehrzahl, denn wenn es viele sind… (Und bitte nichts selbst ausgedachtes, sondern reale Beispiele.)

Dein Testprogramm ist kein Beweis, denn das Verhalten welches Du dort siehst ist von der Sprache Python nicht garantiert.

Über Referenzen steht nichts in der Dokumentation weil die nicht garantiert sind. Die Sprachspezifikation kennt keine Referenzzähler und eine Python-Implementierung muss keine verwenden und es gibt Implementierungen die auch *tatsächlich* keine Verwenden, da stimmen Deine Annahmen über ``del`` dann nicht. Die JVM und damit Jython verwenden zum Beispiel keine Referenzzähler. Alle Sprachen die auf der JVM laufen sind also Unsinn? Da könnte man dann wahrscheinlich keine Serversoftware mit laufen lassen. Oh warte, das machen aber viele…

Selbst in CPython kann es Objekte geben deren Referenzzähler am Ende nicht auf 0 steht, die aber trotzdem Garbage sind, man den Zähler also nicht mehr mit ``del`` verringern kann. *Und* dann kann es sogar sein, dass solche Objekte nie freigegeben werden (können), also erst mit Prozessende freigegeben werden. Und selbst wenn man alles freigeben würde, so wie Du das versuchst, wie es aber nicht garantiert geht, könnte das Programm am Ende wegen Speicherfragmentierung vielleicht doch an Speichermangel sterben, weil zwar genug Speicher da ist, aber keiner in ausreichender Menge *am Stück*. Das kann man alles umgehen in dem man die unabhängigen Anwendungen aus Deinem realitätsfernen Szenario als Prozesse starten würde.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Alfons Mittelmeyer hat geschrieben:Daher kann man keinerlei Dokumentationen ernstlich als verläßliche Quellen für das was nicht drin steht, gebrauchen.
Öh... Du bist also auch so ein Verfechter amerikanischer Mentalität, bei der man *explizit* alles evtl. mit einer Sache mögliche a priori benennen und vor der Zweckentfremdung warnen muss? :roll:

("Ein Tempomat ist kein Autopilot und kann nicht lenken!" :twisted: )
Alfons Mittelmeyer hat geschrieben: Und warum, weiß ich dass man das so implementiert? Weil es etwa auch in .Net so implementiert ist, und weil ich das genauso implementiert habe. Und alles andere Unsinn ist.
Du weißt also Dinge besser als die Python Entwickler? Du hast in den CPython-Quellcode geguckt? (und ggf. in alle anderen, damit Du weißt, wie sich die angebliche Magie hinter ``del`` in anderen Implementierungen verhält?

Und was meinst Du mit .NET? Vermutlich die CLR? Aber inwiefern gibt es zwischen dieser und einem Statement in einer bestimmten Sprache einen Zusammenhang? Oder gehörst Du zu solchen komischen Menschen, die in C# einen Destruktor implemenieren? (Wie im Buch C# in 21 Tagen von 2008 gezeigt und empfohlen :roll: ) Oder was meinst Du damit?

Und was hast Du "genauso" implementiert?

So langsam wird's echt lächerlich... so wenig Selbstreflektion kann man doch kaum haben, wenn der Intellekt zumindest so weit reicht, dass man sich in halbwegs komplexe Programmierprobleme einarbeiten kann. Komisch, da sehe ich eine große Diskrepanz der mentalen Kompetenzen... aber meine Hinweise bezüglich "gegen den Strom schwimmen" ignorierst Du ja auch konsequent 8)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Also, diese ganze Diskussion geht mir jetzt langsam auf den Geist. Wißt Ihr was ich da mache? Ich schreibe einfach eine Funktion delAll() und lösch nach der Ausführung eines Scripts einfach alles raus, ohne dass ich da mit Leuten, die eine skurrile Auffassung haben über das, was del macht, lange darüber diskutiere.

Einfache Lösung, oder?
BlackJack

@Alfons Mittelmeyer: Du bist der mit der skurrilen Auffassung über das was ``del`` macht. Und gehst damit hier bereits vielen auf den Geist. Hör einfach auf als Geisterfahrer unterwegs zu sein. Einfache Lösung, oder?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Du bist der mit der skurrilen Auffassung über das was ``del`` macht. Und gehst damit hier bereits vielen auf den Geist. Hör einfach auf als Geisterfahrer unterwegs zu sein. Einfache Lösung, oder?
Die Sache sollte doch klar sein. Wenn ein Statement 'del' heißt, dann ist davon auszugehen, dass das Löschen bedeutet und das es das auch tut. Und wenn das nicht richtig in der Doku steht, dann ist die Doku unvollständig. Ich habe da keine Kontakte zu Python Core Entwicklern. Also fragt doch Ihr da mal nach, anstatt zu glauben dass del nicht löscht, weil es nicht in der Doku steht.

Außerdem warum schreibt Ihr nicht selber Tests, ob der Referenzzähler um eins runtergeht?
Zuletzt geändert von Alfons Mittelmeyer am Freitag 14. August 2015, 22:34, insgesamt 1-mal geändert.
Benutzeravatar
sparrow
User
Beiträge: 4541
Registriert: Freitag 17. April 2009, 10:28

Jetzt wird es aber arg esoterisch.
Ich mache mir Gedanken, wenn hier mal Neulinge auftauchen und das für richtig halten, was der Alfons Mittelmeyer hier veranstaltet.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

sparrow hat geschrieben:Jetzt wird es aber arg esoterisch.
Ich mache mir Gedanken, wenn hier mal Neulinge auftauchen und das für richtig halten, was der Alfons Mittelmeyer hier veranstaltet.
Sorry ich bin ein Neuling. Beschäftige mich erst seit zwei Monaten mit Python und habe auch noch kein Buch darüber gelesen.

Weil das ist ja einfach. Muß man nur wissen, dass man Funktionen mit def definiert und dahinter einen Doppelpunkt machen muss.
Und bei if und else, darf man auch keine Klammer machen, aber dahinter muß ein Doppelpunkt sein. Und bei for muß dahinter auch ein Doppelpunkt sein.
Und den Code muß man einrücken statt geschweifte Klammmern. Und wenn man sich Listen und Dictionarys angeschaut hat, kann man loslegen.
Und wenn man gesehen hat, dass tkinter nicht besonders umfangreich ist, kann man dann einen GuiEditor sozusagen als mein ersten Python Programm schreiben.
Zuletzt geändert von Alfons Mittelmeyer am Freitag 14. August 2015, 22:46, insgesamt 1-mal geändert.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Icch warte noch auf Antwort, zu:
jens hat geschrieben:
Alfons Mittelmeyer hat geschrieben: Und viele starten nicht immer python neu, sondern beschicken python über den standardinput mit unterschiedlichsten Scripten.
Ach ja?
Welche Beispiele kannst du da nennen?
Und in welche hilft del?

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

jens hat geschrieben:Icch warte noch auf Antwort, zu:
jens hat geschrieben:
Alfons Mittelmeyer hat geschrieben: Und viele starten nicht immer python neu, sondern beschicken python über den standardinput mit unterschiedlichsten Scripten.
Ach ja?
Welche Beispiele kannst du da nennen?
Und in welche hilft del?
Bin ich Dein Neger, dass ich jetzt Firmen in Deutschland abklappere und frage, welche Python einsetzen und in welcher Art. Wenn Du es wissen willst, mach es doch selber.
Und wo hilft da del? Überall, dort wo man Python nicht nach Ausführung eines Scripts beendet, sondern weitere Scripte durchlaufen läßt. Allerdings wird es schwierig sein, dann Programmierer davon zu überzeugen, dass sie mit del aufräumen müssen, weil Python nach Scriptausführung nicht beendet wird. Daher wäre eine Funktion delAll nützlich, die dann alles aufräumt. Die kann ich ja schreiben, nachdem ich meinen GuiEditor fertiggestellt habe.
BlackJack

@Alfons Mittelmeyer: ``del`` löscht, und zwar *Namen*, und Elemente aus Containertypen, beziehungsweise was immer der Datentyp für diese Operation konkret implementiert. Und das steht richtig in der Dokumentation. ``del`` ist nicht für die manuelle Speicherverwaltung gedacht oder geeignet und darum steht das *nicht* in der Dokumentation. Auch wenn Du das noch so oft behauptest mit Deinen zwei Monaten Python-Erfahrung. Da brauche ich keinen Python-Core-Entwickler fragen weil ich das selber aus der Dokumentation und den verschiedenen Implementierungen von Python ableiten kann.

Wie soll ich einen Test auf Referenzzähler schreiben wenn es diese Referenzzähler auf Ebene der Sprache Python gar nicht gibt? Schreib doch bitte mal so einen Test der mit Jython läuft. Wie ermittelst Du den Referenzzähler eines Objekts?

*Du* hast behauptet das viele Python nicht neu starten sondern über stdin mit immer neuen Skripten beschicken. Bei so einer Behauptung kann man erwarten das Du für den Beweis nicht erst *danach* Beispiele suchen musst, sondern die schon vor der Aufstellung der Behauptung hattest.

Es reicht echt langsam!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Da ich ein Python Neuling bin, habe ich mich mit der Python Implementation von Referenzzählern noch nicht befaßt. Ich hatte gedacht dass Ihr das wüßt, wo Ihr doch als Experten auftretet. Tut mir leid, aber ich schreibe Euch keine Tests. Mein Standpunkt ist, wenn Ihr es wissen wollt, dann macht es bitte selber.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Und das mit Firmen, die Python mit Scripten beschicken. Oder auch im anderen Falle, ob wirklich Speicherzumüllen Crashs verursachen könnte. Da erwartet Ihr anscheinend Beweise, Studien und Expertisen, die Jahre und viel Geld kosten könnten. Wo ein eingefügter del Befehl aber nur eine Sekunde dauert oder eine delAll Funktion vielleicht nur einen Tag. Mit Studien und Beweisen vergeudet man nur Zeit und Geld. Loslegen und nicht lange fragen ist wohl die beste und einfachste Alternative.
Benutzeravatar
pillmuncher
User
Beiträge: 1531
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Alfons Mittelmeyer hat geschrieben:Wenn ein Statement 'del' heißt, dann ist davon auszugehen, dass das Löschen bedeutet und das es das auch tut.
Tut es ja auch. Angewendet auf Collections - mittels Subscription - löscht es Elemente und angewendet auf Namen löscht es diese aus dem jeweiligen Namensraum. Die Objekte, auf die die gelöschten Namen (bzw. Indizes bei der Subscription) verweisen, werden dabei nicht gelöscht, denn es könnte ja noch andere Verweise auf diese Objekte geben. Erst, wenn es keine solchen Verweise mehr gibt, entfernt der GC (vielleicht) das Objekt und gibt den Speicher wieder frei.

Der Witz dabei ist übrigens, dass Namensräume in Python Dictionaries sind:

Code: Alles auswählen

In [1]: class C:
   ...:     pass
   ...:

In [2]: c = C()

In [3]: c.x = 123

In [4]: c.__dict__
Out[4]: {'x': 123}

In [5]: del c.x

In [6]: c.__dict__
Out[6]: {}

In [7]: c.x = 456

In [8]: c.__dict__
Out[8]: {'x': 456}

In [9]: del c.__dict__['x']

In [10]: c.__dict__
Out[10]: {}

In [11]: c.x
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-11-1f89b44c0846> in <module>()
----> 1 c.x

AttributeError: 'C' object has no attribute 'x'
Oder hier:

Code: Alles auswählen

>>> x = 123
>>> globals()
{'__doc__': None, '__spec__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__package__': None, 'x': 123}
>>> del globals()['x']
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
In specifications, Murphy's Law supersedes Ohm's.
Gesperrt