RAII in Python – automatische Zerstörung beim Verlassen eines Geltungsbereichs

Ich habe versucht, RAII in Python zu finden. Ressourcenzuordnung Ist Initialisierung ein Muster in C ++, wobei ein Objekt initialisiert wird, wie es erstellt wird. Wenn es scheitert, dann wirft es eine Ausnahme aus. Auf diese Weise weiß der Programmierer, dass das Objekt niemals in einem halbkonstruierten Zustand bleiben wird. Python kann so viel machen.

Aber RAII arbeitet auch mit den Scoping Regeln von C ++, um die sofortige Zerstörung des Objekts zu gewährleisten. Sobald die Variable aus dem Stapel springt, wird sie zerstört. Dies kann in Python passieren, aber nur, wenn es keine externen oder kreisförmigen Referenzen gibt.

Noch wichtiger ist, dass ein Name für ein Objekt noch existiert, bis die Funktion in Exits (und manchmal länger) ist. Variablen auf Modul-Ebene bleiben für die Lebensdauer des Moduls.

Ich hätte gern einen Fehler, wenn ich so etwas mache:

for x in some_list: ... ... 100 lines later ... for i in x: # Oops! Forgot to define x first, but... where's my error? ... 

Ich konnte die Namen manuell löschen, nachdem ich es benutzt habe, aber das wäre ziemlich hässlich und verlange von mir.

Und ich würde es gern tun-was-ich-ja in diesem Fall:

 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 macht einige Scoping, aber nicht auf der Einrückung Ebene, nur auf der funktionalen Ebene. Es scheint albern zu verlangen, dass ich eine neue Funktion mache, nur um die Variablen zu berechnen, damit ich einen Namen wiederverwenden kann.

Python 2.5 hat die "mit" Statement, aber das erfordert, dass ich explizit in __enter__ und __exit__ Funktionen und in der Regel scheint mehr auf die Reinigung von Ressourcen wie Dateien und Mutex-Sperren unabhängig von der Exit-Vektor. Es hilft nicht beim Scoping. Oder vermisse ich etwas

Ich habe nach "Python RAII" und "Python Umfang" gesucht und ich konnte nichts finden, was das Problem direkt und autoritativ angesprochen hat. Ich habe alle PEPs gesehen. Das Konzept scheint nicht in Python angesprochen zu werden.

Bin ich ein schlechter Mensch, weil ich in Python Scoping-Variablen haben möchte? Ist das einfach zu unpythonisch?

Bin ich es nicht grokking?

Vielleicht versuche ich, die Vorteile der dynamischen Aspekte der Sprache wegzunehmen. Ist es egoistisch, dass man manchmal den Umfang erzwingen will?

Bin ich faul, dass ich den Compiler / Dolmetscher willst, um meine fahrlässigen Variablen Wiederverwendungsfehler zu fangen? Nun, ja, natürlich bin ich faul, aber bin ich faul in einer schlechten Weise?

4 Solutions collect form web for “RAII in Python – automatische Zerstörung beim Verlassen eines Geltungsbereichs”

Tl; dr RAII ist nicht möglich, du vermischst es mit Scoping im Allgemeinen und wenn du diese zusätzlichen Scopes vermisst, wirst du wahrscheinlich schlechten Code schreiben.

Vielleicht bekomme ich nicht deine Frage, oder du bekommst keine ganz wichtigen Dinge über Python … Zuerst ist die deterministische Objektzerstörung, die an den Geltungsbereich gebunden ist , in einer Müllsammlung nicht möglich. Variablen in Python sind nur Referenzen. Du würdest nicht wollen, dass ein malloc 'd chunk des Gedächtnisses free ' d, sobald ein Zeiger, der darauf hinweist, aus dem Geltungsbereich geht, würden Sie? Praktische Ausnahme in einigen Fällen, wenn Sie passieren, um Ref-Zählung – aber keine Sprache ist verrückt genug, um die genaue Umsetzung in Stein.

Und selbst wenn Sie Referenzzählung haben, wie in CPython, ist es eine Implementierung Detail. Im Allgemeinen, einschließlich in Python, die verschiedene Implementierungen nicht mit Ref-Zählung hat, sollten Sie Code, als ob jedes Objekt hängt herum, bis Speicher läuft aus.

Wie für Namen, die für den Rest eines Funktionsaufrufs vorhanden sind: Sie können einen Namen aus dem aktuellen oder globalen Bereich über die del Anweisung entfernen. Das hat aber nichts mit manueller Speicherverwaltung zu tun. Es entfernt einfach die Referenz. Das kann oder darf nicht passieren, um das referenzierte Objekt auszulösen, um GC'd zu sein und ist nicht der Punkt der Übung.

  • Wenn Ihr Code lang genug ist, um Namenskonflikte zu verursachen, sollten Sie kleinere Funktionen schreiben. Und verwenden Sie beschreibendere, weniger wahrscheinlich-to-clash Namen. Dasselbe für verschachtelte Loops, die die Iterationsvariable der Out-Loop überschreiben: Ich bin noch in dieses Problem zu laufen, also vielleicht sind deine Namen nicht beschreibend genug oder du solltest diese Loops auseinander fassen?

Sie sind richtig, with hat nichts mit Scoping zu tun, nur mit deterministischer Aufräumung (also überlappt es mit RAII in den Enden, aber nicht in den Mitteln).

Vielleicht versuche ich, die Vorteile der dynamischen Aspekte der Sprache wegzunehmen. Ist es egoistisch, dass man manchmal den Umfang erzwingen will?

Nr. Anständiges lexikalisches Scoping ist ein Verdienst unabhängig von Dynamik / Statik. Zugegeben, Python (2 – 3 so ziemlich fixiert) hat Schwächen in dieser Hinsicht, obwohl sie mehr im Bereich der Schließungen sind.

Aber um zu erklären, "warum": Python muss konservativ sein, wo es einen neuen Geltungsbereich anfängt, denn ohne Deklaration sagt anders, die Zuordnung zu einem Namen macht es zu einem lokalen zum innersten / aktuellen Umfang. Also zB wenn eine for-Schleife hatte es eigenen Bereich, konnte man nicht leicht ändern Variablen außerhalb der Schleife.

Bin ich faul, dass ich den Compiler / Dolmetscher willst, um meine fahrlässigen Variablen Wiederverwendungsfehler zu fangen? Nun, ja, natürlich bin ich faul, aber bin ich faul in einer schlechten Weise?

Auch hier stelle ich mir vor, dass die Beanstandung eines Namens (in einer Weise, die Fehler oder Fallstricke einbringt) selten und eine kleine sowieso ist.

Bearbeiten: Um dies wieder so deutlich wie möglich zu machen:

  • Es kann keine Stack-basierte Bereinigung in einer Sprache mit GC geben. Es ist einfach nicht möglich, per definitionem: eine Variable ist eine von potenziell vielen Verweisen auf Objekte auf dem Haufen, die weder wissen noch kümmern, wenn Variablen aus dem Geltungsbereich gehen, und alle Speicherverwaltung liegt in den Händen des GC, die läuft, wenn es Mag es nicht, wenn ein Stapelrahmen geknallt wird. Resource Cleanup ist anders gelöst, siehe unten.
  • Deterministische Bereinigung geschieht durch die with Aussage. Ja, es gibt keinen neuen Bereich (siehe unten), denn das ist nicht das, was es ist. Es spielt keine Rolle, dass es den Namen entfernt hat, an dem das verwaltete Objekt gebunden ist, wird nicht entfernt – die Aufräumung ist dennoch passiert, was bleibt, ist ein "nicht berühre mich, ich bin unbrauchbar" (zB ein geschlossener Datei-Stream).
  • Python hat einen Bereich pro Funktion, Klasse und Modul. Periode. So funktioniert die Sprache, ob es dir gefällt oder nicht. Wenn du willst, "brauche" mehr feinkörniges Scoping, breche den Code in feinere Körner. Vielleicht wünschst du dir noch feinkörnigeres Scoping, aber es gibt nicht – und aus Gründen, die früher in dieser Antwort (drei Absätze über dem "Edit:") hervorgehoben wurden, gibt es Gründe dafür. Wie es oder nicht, aber das ist, wie die Sprache funktioniert.
  1. Sie haben Recht with – es ist völlig unabhängig von variablen Scoping.

  2. Vermeiden Sie globale Variablen, wenn Sie denken, dass sie ein Problem sind. Dazu gehören Modul-Level-Variablen.

  3. Das wichtigste Werkzeug, um Zustand in Python zu verstecken sind Klassen.

  4. Generatorausdrücke (und in Python 3 auch Listenverständnisse) haben ihren eigenen Umfang.

  5. Wenn Ihre Funktionen lang genug für Sie sind, um die lokalen Variablen zu verlieren, sollten Sie wahrscheinlich Ihren Code umgestalten.

Aber RAII arbeitet auch mit den Scoping Regeln von C ++, um die sofortige Zerstörung des Objekts zu gewährleisten.

Dies gilt als unwichtig in GC-Sprachen, die auf der Idee basieren, dass Gedächtnis fungibel ist . Es gibt keine dringende Notwendigkeit, das Gedächtnis eines Objekts zurückzugewinnen, solange es genügend Arbeitsspeicher gibt, um neue Objekte zuzuordnen. Nicht-fungible Ressourcen wie Aktengriffe, Sockets und Mutexe gelten als Spezialfall, der speziell behandelt werden soll (zB with ). Dies steht im Gegensatz zu C ++ – Modell, das alle Ressourcen gleich behandelt.

Sobald die Variable aus dem Stapel springt, wird sie zerstört.

Python hat keine Stapelvariablen. In C ++ Begriffen ist alles ein shared_ptr .

Python macht einige Scoping, aber nicht auf der Einrückung Ebene, nur auf der funktionalen Ebene. Es scheint albern zu verlangen, dass ich eine neue Funktion mache, nur um die Variablen zu berechnen, damit ich einen Namen wiederverwenden kann.

Es macht auch Scoping auf der Generator-Verständnis Ebene (und in 3.x, in allen Verständnissen).

Wenn du es nicht willst, dich for Loop-Variablen zu verschlingen, benutze nicht so viele for Loops. Insbesondere ist es un-Pythonic zu verwenden append in einer Schleife. Anstatt von:

 new_points = [] for x,y,z in surface.points: ... # Do something with the points new_points.append( (x,y,z) ) 

schreiben:

 new_points = [do_something_with(x, y, z) for (x, y, z) in surface.points] 

oder

 # Can be used in Python 2.4-2.7 to reduce scope of variables. new_points = list(do_something_with(x, y, z) for (x, y, z) in surface.points) 

Grundsätzlich sind Sie wahrscheinlich mit der falschen Sprache. Wenn Sie gesunde Scoping Regeln und zuverlässige Zerstörung wollen dann mit C + + oder versuchen Perl. Die GC-Debatte darüber, wann das Gedächtnis freigegeben wird, scheint den Punkt zu verpassen. Es geht darum, andere Ressourcen wie Mutexe und Dateihandles freizugeben. Ich glaube, C # macht die Unterscheidung zwischen einem Destruktor, der aufgerufen wird, wenn die Referenzzählung auf Null geht und wenn es beschließt, den Speicher zu recyceln. Die Leute sind nicht so besorgt über die Erinnerung Recycling aber wollen wissen, sobald es nicht mehr referenziert wird. Es ist schade, denn Python hatte echtes Potenzial als Sprache. Aber es ist unkonventionelle Scoping und unzuverlässige Destruktoren (oder zumindest implementierungsabhängige) bedeutet, dass man die Macht, die man mit C ++ und Perl bekommt, verweigert wird.

Interessant der Kommentar über nur mit neuen Speicher, wenn es verfügbar ist, anstatt Recycling alt in GC. Ist das nicht nur eine fancy Art zu sagen, es leckt Speicher 🙂

Python ist die beste Programmiersprache der Welt.