RAII in Python - automatische Zerstörung beim verlassen eines Bereichs
Ich habe versucht zu finden, RAII in Python.
Resource Allocation Is Initialization " ist ein Muster in C++, wobei
ein Objekt initialisiert wird, wie es erstellt wird. Wenn es scheitert, dann wirft er
eine Ausnahme. Auf diese Weise wird der Programmierer weiß, dass
das Objekt wird nie verlassen werden, in ein halb-fertiggestellten Zustand. Python
kann so viel tun.
Aber RAII funktioniert auch mit den scoping-Regeln von C++
zur Gewährleistung der Eingabeaufforderung Zerstörung des Objekts. Sobald die variable
off erscheint der stack zerstört wird. Dies kann geschehen, in Python, aber nur
wenn es keine externen oder zirkuläre Referenzen.
Wichtiger ist, einen Namen für ein Objekt noch vorhanden ist, bis die Funktion ist es
in-und Ausgänge (und manchmal mehr). Variablen auf Modulebene werden
bleiben, um für die Lebensdauer des Moduls.
Möchte ich erhalten eine Fehlermeldung, wenn ich etwas wie das hier tun:
for x in some_list:
...
... 100 lines later ...
for i in x:
# Oops! Forgot to define x first, but... where's my error?
...
Konnte ich manuell löschen, den Namen nach, ich habe es verwendet,
aber das wäre ziemlich hässlich, und erfordern Anstrengung auf meinem Teil.
Und ich würde es gerne Tun-Was-ich-in diesem Fall Bedeuten:
for x in some_list:
surface = x.getSurface()
new_points = []
for x,y,z in surface.points:
... # Do something with the points
new_points.append( (x,y,z) )
surface.points = new_points
x.setSurface(surface)
Python hat einige scoping, nicht aber an der Einrückung, nur an
die funktionale Ebene. Es scheint albern zu verlangen, dass ich eine neue Funktion
nur um den Umfang der Variablen, so kann ich die Wiederverwendung einen Namen.
Python 2.5 hat die der "with" - Anweisung
aber das erfordert, dass ich explizit setzen in __enter__
und __exit__
Funktionen
und generell scheint sich stärker in Richtung Bereinigung von Ressourcen wie Dateien
und mutex-sperren, unabhängig von der Ausfahrt Vektor. Es hilft nicht, mit der Festlegung des lösungsumfangs.
Oder bin ich etwas fehlt?
Ich gesucht habe für "Python RAII" und "Python-scope" und ich war nicht in der Lage, etwas zu finden, dass
die Frage direkt und verbindlich.
Ich habe gesucht über alle PEPs. Das Konzept scheint nicht angesprochen zu werden
in Python.
Bin ich ein schlechter Mensch, weil ich scoping von Variablen in Python?
Ist das einfach nur zu un-Pythonic?
Bin ich nicht grokking es?
Vielleicht bin ich versucht zu nehmen die Vorteile der dynamischen Aspekte der Sprache.
Ist es egoistisch, manchmal wollen Umfang durchgesetzt?
Bin ich zu faul für den Wunsch der compiler/interpreter
fangen meine fahrlässigen variable Wiederverwendung Fehler? Gut, ja, natürlich bin ich faul,
aber bin ich faul in einem schlechten Weg?
- RAII ist äquivalent zu Python
with
Aussage (ich bin eigentlich überrascht, dass die Abkürzung nicht in PEP 343 - es war sicherlich geworfen, um eine Menge in den zugehörigen Diskussionen). Das fehlende element ist Sie für Fragen ist der anonyme scoping-Regeln von C/C++ und kein Python nicht haben (obwohl Sie möglicherweise die latente PEP 3150 von Interesse). Das ist völlig unabhängig von der RAII Frage, obwohl. - Danke für die Antwort. Ich dachte, PEP 3150 wurde die Beantwortung meiner Frage, aber dann merkte ich, dass es sagt nichts über das entfernen des Namespaces. Es ist rein zum verschieben der Aufruf der Funktion über den code, bekommt Werte für die Parameter, so dass es mehr als offensichtlich, was der code leistet. Was ich nicht mag, über
with
ist, dass der name nicht Weggehen, und ich habe zu bewegen-code in__exit__
statt__del__
. Gibt es vielleicht eine geschickte wrapper-Objekt, dass würdedel
den Namen des Objekts und nicht verlangen, dass ich code ein__exit__
Funktion? Idealerweise würde behaupten, dass die ref-Anzahl 0 war. - auch wenn der refcount > 0, kann man
del
den Namen des Objekts aus Ihrem eigenen Bereich und loszuwerden, die Namen - das ist völlig getrennt von tatsächlich zerstören das Objekt. Zum Beispiel, wenn ein Objekt eingesetzt worden war, in einen Behälter, dessen refcount wäre > 0, aber man konnte nochdel
den Namen, den Sie verwendet, um sich auf das Objekt selbst. Vielleicht ist das auch Teil deiner Verwirrung -del
löscht den Namen, nicht auf das zugrunde liegende Objekt. Dies ist eine Unterscheidung in Python, C/C++ - Leute kämpfen mit - Sie sind den Umgang mit Namen und Bindungen auf die Namen, nicht die Variablen/Verweise alloc ' ed und befreit.
Du musst angemeldet sein, um einen Kommentar abzugeben.
tl;dr RAII ist nicht möglich, mischen Sie es mit der Festlegung des lösungsumfangs im Allgemeinen und, wenn Sie vermisse die extra-Bereiche, die du wahrscheinlich schreiben schlechten code.
Vielleicht habe ich nicht Ihre Frage(N), oder Sie nicht bekommen, einige sehr wesentliche Dinge über Python... First off, deterministische Objekt-Zerstörung gebunden Umfang ist unmöglich in ein garbage Collection-Sprache. Variablen in Python sind lediglich Verweise. Sie würde nicht wollen, eine
malloc
'd chunk-Speicher zufree
'd, sobald ein Zeiger auf es geht out of scope, würden Sie? Praktische Ausnahme in einige Umständen, wenn Sie geschehen, ref zu zählen - aber keine Sprache ist verrückt genug, um die genaue Umsetzung in Stein.Und auch wenn Sie haben die Verweiszählung, wie in CPython, es ist eine Implementierung detail. In der Regel, auch in Python, die mit verschiedenen Implementierungen nicht mit ref-counting, sollten Sie code, als wenn jedes Objekt hängt herum, bis der Speicher ausgeht.
Als für die bestehenden Namen für den rest ein Funktions-Aufruf: Sie kann entfernen eines namens aus der aktuellen oder globalen Rahmen über die
del
- Anweisung. Aber das hat nichts zu tun mit der manuellen Speicherverwaltung. Es entfernt nur die Referenz. Das kann oder kann nicht passieren, um die trigger-das referenzierte Objekt von der GC würde und das ist nicht der Sinn der übung.Sind Sie richtig,
with
hat nichts zu tun mit der Festlegung des lösungsumfangs, nur mit deterministischen Aufräumen (also es überschneidet sich mit RAII in den enden, aber nicht im Mittel).Nicht. Anständige lexikalische scoping ist ein Verdienst unabhängig von der Dynamik-/staticness. Zugegeben, Python (2 - 3 so ziemlich behoben) hat Schwächen in dieser Hinsicht, obwohl Sie eher in den Bereich der Verschlüsse.
Aber um zu "erklären " warum": Python muss werden konservativ mit wo es beginnt, einen neuen Bereich, weil ohne Erklärung sagen, sonst Zuordnung zu einem Namen macht es zu einem lokal auf der innersten/aktuellen Bereich. So z.B., wenn eine for-Schleife hatte Ihren eigenen Bereich, konnte man nicht leicht ändern Sie die Variablen außerhalb der Schleife.
Wieder, ich kann mir vorstellen, dass die zufälligen resuse einen Namen (in einer Weise, stellt Fehler oder Fallstricke) ist selten, und die kleine sowieso.
Edit: Zu behaupten das dies wieder so deutlich wie möglich:
with
- Anweisung. Ja, es macht auch nicht vorstellen einen neuen Bereich (siehe unten), denn das ist nicht das, was es ist. Es spielt keine Rolle, es entfernt den Namen des verwalteten Objekts gebunden ist, nicht entfernt - die Bereinigung passiert ist dennoch, was bleibt, ist ein "don' T touch me ich bin unbrauchbar" Objekt (z.B. eine geschlossene Datei-stream).with
ist für. Weil resource cleanup sollte in__exit__
oder, wenn Sie eine Arme Seele, die nie gehört von Kontext-Manager, in einigen extern verwaltbare Ressource. GC ist so ziemlich per definition nicht deterministisch, und Sie nicht bekommen Python zum ausführen einer face-heel turn und re-vorstellen die manuelle Speicherverwaltung oder einige leicht entladen Variante. Die Arbeit mit der Sprache, wie Sie ist, oder ein anderes verwenden. Was würden Sie denken über die Anforderungen für die GC in C++?__exit__
ist ein schlechter Ersatz, da ich die Namen Weg, ohne einedel
, und__exit__
ist nicht genannt, mit Ausnahme vonwith
. Es könnte sein, viel einfacher.with
ist der Weg zu gehen für deterministische Bereinigung. Und obwohl der name bleibt nach derwith
block fertig, das cleanup ist erledigt und damit hast du den gleichen Effekt angewendet wie RAII, wenn auch durch andere Mittel. Oder tun Sie missbrauchen den Begriff RAII und eigentlich nur wollen, mehr feinkörnige scoping?with
aber nach der anfänglichen Aufregung, die ich erkannte, es war nicht das, was ich suchte. Ich bin zurückgetreten, um Python keine entsprechenden stack-based scoping-alternative (mit den Namen). Vielleicht sollte ich zu entfernen RAII von meiner Frage, aber ich habe das Gefühl es ist ein ganzheitliches Konzept, um das Problem des name-Rahmen. Die Frage hat zwei Beispiele, die belegen beide die Ausgabe der Variablen-name nicht Weg. Danke für die Hilfe, aber. Zumindest jetzt weiß ich, ich bin nicht etwas fehlt offensichtlich.with
und es ist nicht das, was ich will, aber ich denke, das ist der "Pythonic way", die ein Teil meiner Frage. BTW, dein letzter Punkt scheint widersprochen werden, die durch den generator scope und Liste Verstehens haben Ihren eigenen Bereich in Python 3000 (siehe andere Antworten). Dinge zu sein scheinen trending meinen Weg. Diese auch ungültig macht Ihren ersten Punkt, da ich nicht Nachfrage stack-basierten implemetation, nur Verhalten. Lassen Sie uns einfach damit einverstanden, dass ich bin stecken in einer C++ - Denkweise, und ich will die Namen gehen aus Umfang und die Python nicht erfüllen, mich gibt es noch. Ich bin unPythonic. 🙁__del__
Dokumentation] erwähnt, (1)del
gar nicht garantieren, dass__del__
aufgerufen wird (wenn es andere Verweise) und (2) zirkuläre Referenzen werden nicht bereinigt, die von refcounting allein.Sie sind Recht
with
-- es ist völlig unabhängig von Variablen (scoping).Vermeiden, Globale Variablen, wenn Sie denken, Sie sind ein problem. Dies beinhaltet die Modul-Ebene-Variablen.
Das wichtigste Instrument, um ausblenden-Status in Python sind Klassen.
Generator Ausdrücke (und in Python 3 auch eine Liste Verstehens) haben Ihren eigenen Bereich.
Wenn Ihre Funktionen sind lang genug für Sie, um den überblick verlieren die lokalen Variablen, sollte man wohl umgestalten von code.
Ist dies als unwichtig in GC-Sprachen, die auf der Idee basiert, dass der Speicher ist fungibel. Es gibt keine dringende Notwendigkeit, um zurückzugewinnen, die das Objekt den Speicher, solange es genügend Speicherplatz anderweitig zuzuweisen neue Objekte. Nicht-vertretbare Ressourcen wie Datei-handles, sockets und Mutexe sind als ein Sonderfall behandelt werden, die speziell (z.B.
with
). Dies kontrastiert mit C++'s Modell behandelt alle Ressourcen die gleiche.Python nicht stack-Variablen. In C++ Begriffe, alles ist ein
shared_ptr
.Es auch hat scoping am generator comprehension level (und in 3.x, in alle Verstehens).
Wenn Sie nicht wollen, um clobber Ihre
for
Schleife Variablen nicht so vielefor
Schleifen. Insbesondere ist es un-Pythonic zu verwendenappend
in einer Schleife. Statt:schreiben:
oder
Grundsätzlich sind Sie wahrscheinlich mit der falschen Sprache. Wenn Sie wollen vernünftige scoping-Regeln und zuverlässige Vernichtung dann stick mit C++ oder Perl versuchen. Die GC Debatte über, wenn der Speicher freigegeben wird, scheint auf den Punkt verpassen. Es ist über die Freigabe von anderen Ressourcen wie Mutexe und Datei-handles. Ich glaube, dass C# macht den Unterschied zwischen einen Destruktor, der aufgerufen wird, wenn der Referenzzähler auf null geht und wenn er entscheidet, zu recyceln die Erinnerung. Die Menschen sind nicht so besorgt über die Speicher-recycling, sondern wollen wissen, sobald es nicht mehr referenziert wird. Es ist schade, da Python hatte echt Potenzial als eine Sprache. Aber es ist unkonventionell scoping und unzuverlässig Destruktoren (oder zumindest die Umsetzung abhängig sind) bedeutet, dass man verweigert die Leistung bekommst du mit C++ und Perl.
Interessant die Bemerkung über die nur mit neuen Speicher, wenn es verfügbar ist, anstatt das recycling von alten GC. Ist das nicht nur eine ausgefallene Art zu sagen, es Lecks Speicher 🙂
Beim Wechsel zu Python nach Jahren von C++, die ich gefunden habe, ist es verlockend, auf die Sie sich verlassen
__del__
zu imitieren, RAII-Typ Verhalten, z.B. zum schließen von Dateien oder verbindungen. Es gibt jedoch Situationen (z.B. observer-pattern implementiert werden, indem Rx), wo die Sache beobachtet, der verwaltet eine Referenz auf das Objekt, halten Sie es lebendig! Also, wenn Sie wollen, um die Verbindung zu schließen, bevor Sie beendet wird, durch die Quelle, die Sie nicht überall zu bekommen, indem Sie versuchen zu tun, dass in__del__
.Folgende situation entsteht, in der UI-Programmierung:
So, hier ist Weg, um RAII-Typ-Verhalten: legen Sie einen container mit hinzufügen und entfernen von Haken:
Wenn
UiComponent.children
ist einScopedList
fordertacquire
unddispose
Methoden auf den Kindern, Sie bekommen die gleiche Garantie von deterministischen Ressource Erwerb und Veräußerung, wie Sie verwendet werden, um in C++.