Wann ist del in Python nützlich?

374

Ich kann mir keinen Grund vorstellen, warum Python das delSchlüsselwort benötigt (und die meisten Sprachen scheinen kein ähnliches Schlüsselwort zu haben). Anstatt beispielsweise eine Variable zu löschen, könnte man Nonesie einfach zuweisen . Beim Löschen aus einem Wörterbuch kann eine delMethode hinzugefügt werden.

Gibt es einen Grund, delin Python zu bleiben , oder ist es ein Überbleibsel von Pythons Tagen vor der Müllabfuhr?

Jason Baker
quelle
46
Historischer Hinweis : Python hatte von Anfang an eine Speicherbereinigung. Vor 2.0 konnte Pythons Garbage Collector keine Referenzzyklen erkennen, aber das hatte nichts damit zu tun del.
Steven Rumbalski
28
@Steven Rumbalksi, es hat etwas mit del zu tun. Del wurde verwendet, um Referenzzyklen zu unterbrechen.
Winston Ewert
6
Aber es delist kein Überbleibsel der Sammlung vor dem Müll, weil man es immer haben könnte used = None. Es ist einfach immer sinnvoll, eine bestimmte Syntax dafür zu haben. Da wir jetzt zylische GC haben, sind die Fälle, in denen Sie beides verwenden möchten, klein.
Winston Ewert
3
> und die meisten Sprachen scheinen kein ähnliches Schlüsselwort zu haben. Die meisten in Müll gesammelten Sprachen haben dies nicht (oder verwenden es nur, um den GC darauf hinzuweisen, dass es eine Variable sammeln kann). Die meisten älteren Sprachen haben: - Gute alte BASIC-Sprachen wie QuickBasic hatten ERASE(bestimmte Variablen töten) und CLEAR( alle Variablen töten )
DrYak

Antworten:

499

Erstens können Sie neben lokalen Variablen auch andere Dinge löschen

del list_item[4]
del dictionary["alpha"]

Beides sollte eindeutig nützlich sein. Zweitens delmacht die Verwendung einer lokalen Variablen die Absicht klarer. Vergleichen Sie:

del foo

zu

foo = None

Ich weiß, dass in diesem Fall del foodie Absicht besteht, die Variable aus dem Gültigkeitsbereich zu entfernen. Es ist nicht klar, dass das so foo = Noneist. Wenn jemand gerade zugewiesen hat, foo = Nonekönnte ich denken, dass es toter Code war. Aber ich weiß sofort, was jemand, der codiert, del fooversucht hat.

Winston Ewert
quelle
51
+1, ja. Wenn Sie etwas zuweisen, vermittelt es die Absicht, es später zu verwenden. Ich habe es nur wirklich delin speicherintensiven Berechnungen gesehen, aber als ich es sah, wurde mir sofort klar, warum es notwendig war.
Detly
17
Der Anwendungsfall des Löschens aus einer Liste oder einem Wörterbuch könnte leicht durch eine Methode ersetzt werden (wie ich in der Frage bemerkt habe). Ich bin mir nicht sicher, ob ich der Verwendung von " Signalisieren von Absichten" zustimmedel (da ein Kommentar dasselbe tun könnte, ohne die Sprache zu erweitern), aber ich denke, das ist die beste Antwort.
Jason Baker
6
@JasonBaker, gewährt auf die Methoden. Das Löschen von Slices und dergleichen wäre jedoch mit einer Methode umständlicher. Ja, Sie könnten einen Kommentar verwenden. Aber ich denke, eine Aussage zu verwenden ist besser als ein Kommentar als Teil der Sprache.
Winston Ewert
2
@ JasonBaker: Es geht nicht nur um die Absicht, diese beiden Syntaxen machen zwei sehr unterschiedliche Dinge.
Pavel Šimerda
2
Es wird auch durch Löschen aus dem jeweiligen Wörterbuch behandelt. Auf len()die gleiche Weise können Sie die Funktion auch bestreiten . Wenn dies der Fall ist, wurde die Frage möglicherweise als meinungs- oder sogar geschmacksbezogen geschlossen. Python tendiert einfach dazu, Grundelemente für grundlegende Operationen bereitzustellen, anstatt sich auf Methoden zu verlassen.
Pavel Šimerda
161

Es gibt diesen Teil dessen, was delfunktioniert (aus der Python-Sprachreferenz ):

Durch Löschen eines Namens wird die Bindung dieses Namens aus dem lokalen oder globalen Namespace entfernt

Durch das Zuweisen Nonezu einem Namen wird die Bindung des Namens nicht aus dem Namespace entfernt.

(Ich nehme an, es könnte eine Debatte darüber geben, ob das Entfernen einer Namensbindung tatsächlich nützlich ist , aber das ist eine andere Frage.)

Greg Hewgill
quelle
22
-1 Das Poster versteht das eindeutig schon, er fragt, warum Sie eine Namensbindung entfernen möchten.
Winston Ewert
71
@ Winston Ewert: Ich bin nicht sicher, ob das Poster verstanden hat, dass deleine Namensbindung entfernt wird, da er vorgeschlagen hat, sie Noneals Alternative zuzuweisen .
Steven Rumbalski
8
@Steven, das Poster kontrastiert deutlich mit dem Löschen der Variablen (Entfernen des Namens) und dem Zuweisen von None. Er sieht nicht, warum Sie die Variable löschen sollten, wenn Sie einfach Keine zuweisen können. Sie haben den gleichen Effekt, indem sie den Verweis auf alles freigeben, was zuvor an diesen Namen gebunden war.
Winston Ewert
19
@ Winston Ewert: Es ist mir nicht klar. Vielleicht ist es Ihnen klar, dass Sie angeben, dass sie "den gleichen Effekt haben, indem sie den Verweis auf das veröffentlichen, was zuvor an diesen Namen gebunden war". Aber das ist (eindeutig?) Nicht die ganze Geschichte, da ein Versuch, einen Namen nach dem Löschen zu verwenden, a auslöst NameError. Greg Hewgill macht genau diesen Unterschied. Und es ist diese Unterscheidung, die Ihre Behauptung dessen, was das Plakat "klar" verstanden hat, für mich unklar macht.
Steven Rumbalski
9
@ Winston Ewert Ich stimme nicht zu. Aber genug gesagt. Wir haben beide unsere Fälle gemacht.
Steven Rumbalski
44

Ein Ort, den ich für delnützlich befunden habe, ist das Bereinigen von externen Variablen in for-Schleifen:

for x in some_list:
  do(x)
del x

Jetzt können Sie sicher sein, dass x undefiniert ist, wenn Sie es außerhalb der for-Schleife verwenden.

Jason Baker
quelle
1
Soll Ihre delZeile hier eingerückt sein (dh Teil der Schleife)?
Sam
11
@ Sam, nein, sie sind nicht beabsichtigt.
user5319825
7
@ Sam Nein, die Idee hier ist, dass nach der letzten Iteration der Schleife (nach dem Ausführen von do () für das letzte Element von some_list) x eine Referenz auf den endgültigen Wert von some_list bleibt. del x stellt sicher, dass dies nicht als solches zugewiesen bleibt.
Matthew
12
Dies führt dazu, NameError: name 'x' is not definedwenn die Liste leer ist.
WofWca
18

Das Löschen einer Variablen unterscheidet sich vom Festlegen auf Keine

Das Löschen von Variablennamen mit delwird wahrscheinlich nur selten verwendet, ist jedoch ohne ein Schlüsselwort nicht trivial zu erreichen. Wenn Sie einen Variablennamen durch Schreiben erstellen können, a=1ist es schön, dass Sie dies theoretisch rückgängig machen können, indem Sie a löschen.

In einigen Fällen kann das Debuggen vereinfacht werden, da beim Versuch, auf eine gelöschte Variable zuzugreifen, ein NameError ausgelöst wird.

Sie können Klasseninstanzattribute löschen

Mit Python können Sie Folgendes schreiben:

class A(object):
    def set_a(self, a):
        self.a=a
a=A()
a.set_a(3)
if hasattr(a, "a"):
    print("Hallo")

Wenn Sie einer Klasseninstanz dynamisch Attribute hinzufügen möchten, möchten Sie diese auf jeden Fall durch Schreiben rückgängig machen können

del a.a
Bernhard
quelle
16

Es gibt ein spezielles Beispiel dafür, wann Sie eine Ausnahme untersuchen sollten del(es kann andere geben, aber ich weiß dies sofort) sys.exc_info(). Diese Funktion gibt ein Tupel, den Typ der ausgelösten Ausnahme, die Nachricht und einen Traceback zurück.

Die ersten beiden Werte reichen normalerweise aus, um einen Fehler zu diagnostizieren und darauf zu reagieren, aber der dritte enthält den gesamten Aufrufstapel zwischen dem Ort, an dem die Ausnahme ausgelöst wurde, und dem Ort, an dem die Ausnahme abgefangen wurde. Insbesondere, wenn Sie so etwas tun

try:
    do_evil()
except:
    exc_type, exc_value, tb = sys.exc_info()
    if something(exc_value):
        raise

Der Traceback tblandet in den Lokalen des Aufrufstapels und erstellt einen Zirkelverweis, der nicht durch Müll gesammelt werden kann. Daher ist es wichtig zu tun:

try:
    do_evil()
except:
    exc_type, exc_value, tb = sys.exc_info()
    del tb
    if something(exc_value):
        raise

den Zirkelverweis zu brechen. In vielen Fällen , in denen Sie anrufen wollen würde sys.exc_info(), wie mit metaclass Magie, die Zurückverfolgungs ist nützlich, so dass Sie sicherstellen müssen , dass Sie es aufzuräumen , bevor Sie möglicherweise die Exception - Handler verlassen können. Wenn Sie den Traceback nicht benötigen, sollten Sie ihn sofort löschen oder einfach Folgendes tun:

exc_type, exc_value = sys.exc_info()[:2]

Um alles zusammen zu vermeiden.

SingleNegationElimination
quelle
18
Es ist nicht mehr wahr, dass der Müllsammler es nicht sammelt. Der Zyklus verzögert jedoch die Erfassung.
Winston Ewert
Dies ist nicht zu relevant und beantwortet die Frage der Operation nicht.
WhyNotHugo
15

Nur ein anderes Denken.

Beim Debuggen von http-Anwendungen in einem Framework wie Django kann der Aufrufstapel voller nutzloser und durcheinandergebrachter Variablen, die zuvor verwendet wurden, insbesondere wenn es sich um eine sehr lange Liste handelt, für Entwickler sehr schmerzhaft sein. Daher könnte an dieser Stelle die Namespace-Steuerung nützlich sein.

tdihp
quelle
12

Die explizite Verwendung von "del" ist auch besser als das Zuweisen einer Variablen zu None. Wenn Sie versuchen, eine nicht vorhandene Variable zu löschen, wird ein Laufzeitfehler angezeigt. Wenn Sie jedoch versuchen, eine nicht vorhandene Variable auf None zu setzen, setzt Python stillschweigend eine neue Variable auf None, sodass Sie die Variable verlassen wollte gelöscht wo es war. Del hilft Ihnen also, Ihre Fehler früher zu erkennen

Matt
quelle
11

Um den obigen Antworten einige Punkte hinzuzufügen: del x

Die Definition von xzeigt an r -> o(eine Referenz, rdie auf ein Objekt zeigt o), del xändert sich jedoch reher als o. Es ist eine Operation an der Referenz (dem Zeiger) auf ein Objekt und nicht an dem Objekt, das diesem zugeordnet ist x. Die Unterscheidung zwischen rund oist hier der Schlüssel.

  • Es entfernt es aus locals().
  • Entfernt es von globals()wenn es xdort hingehört .
  • Entfernt es aus dem Stapelrahmen (entfernt die Referenz physisch aus dem Stapelrahmen, aber das Objekt selbst befindet sich im Objektpool und nicht im Stapelrahmen).
  • Entfernt es aus dem aktuellen Bereich. Es ist sehr nützlich, die Definitionsspanne einer lokalen Variablen zu begrenzen, da dies sonst zu Problemen führen kann.
  • Es geht eher um die Deklaration des Namens als um die Definition des Inhalts.
  • Es wirkt sich darauf aus, wo es xhingehört, nicht darauf, wohin es xzeigt. Die einzige physische Änderung im Gedächtnis ist dies. Wenn es sich beispielsweise xin einem Wörterbuch oder einer Liste befindet, wird es (als Referenz) von dort entfernt (und nicht unbedingt aus dem Objektpool). In diesem Beispiel ist das Wörterbuch, zu dem es gehört, der Stapelrahmen ( locals()), der sich mit überlappt globals().
Sohail Si
quelle
6

Erzwingen Sie das Schließen einer Datei nach Verwendung von numpy.load:

Eine Nischenverwendung vielleicht, aber ich fand es nützlich, wenn numpy.loadich eine Datei lese. Hin und wieder aktualisierte ich die Datei und musste eine Datei mit demselben Namen in das Verzeichnis kopieren.

Früher habe ich deldie Datei freigegeben und mir erlaubt, sie in die neue Datei zu kopieren.

Hinweis Ich möchte den withKontextmanager vermeiden, da ich mit Plots in der Befehlszeile herumgespielt habe und nicht viel auf die Tabulatortaste drücken wollte!

Siehe diese Frage.

atomh33ls
quelle
Ich hatte etwas Ähnliches mit geladenen Bildern mit der Python Image Library (PIL). Ich öffne ein Bild und wenn es bestimmte Abmessungen hat, möchte ich die Datei löschen. Die Datei wurde jedoch noch von Python verwendet. Deshalb sagte ich 'del img' und konnte dann die Datei entfernen.
körperliche
2
Beachten Sie, dass dies ein Implementierungsdetail ist, da die Sprachspezifikation nicht garantiert, wann die __del__()Methode für Garbage-Objekte aufgerufen wird oder ob sie überhaupt aufgerufen wird. APIs, die keine andere Möglichkeit bieten, Ressourcen freizugeben, als zu hoffen, dass die __del__()Methode irgendwann aufgerufen wird (kurz nachdem das Objekt Müll ist), sind bis zu einem gewissen Grad fehlerhaft.
BlackJack
6

delwird oft in __init__.pyDateien gesehen. Jede globale Variable, die in einer __init__.pyDatei definiert ist, wird automatisch "exportiert" (sie wird in a aufgenommen from module import *). Eine Möglichkeit, dies zu vermeiden, besteht darin, zu definieren __all__, aber dies kann chaotisch werden und wird nicht von jedem verwendet.

Zum Beispiel, wenn Sie Code in __init__.pylike hatten

import sys
if sys.version_info < (3,):
    print("Python 2 not supported")

Dann würde Ihr Modul den sysNamen exportieren . Sie sollten stattdessen schreiben

import sys
if sys.version_info < (3,):
    print("Python 2 not supported")

del sys
asmeurer
quelle
4

Als Beispiel dafür, wofür delverwendet werden kann, finde ich es in Situationen wie diesen nützlich:

def f(a, b, c=3):
    return '{} {} {}'.format(a, b, c)

def g(**kwargs):
    if 'c' in kwargs and kwargs['c'] is None:
        del kwargs['c']

    return f(**kwargs)

# g(a=1, b=2, c=None) === '1 2 3'
# g(a=1, b=2) === '1 2 3'
# g(a=1, b=2, c=4) === '1 2 4'

Diese beiden Funktionen können in verschiedenen Paketen / Module und der Programmierer muss nicht wissen , was Argument Standardwert cin ftatsächlich haben. Wenn Sie also kwargs in Kombination mit del verwenden, können Sie "Ich möchte den Standardwert für c" sagen, indem Sie ihn auf "Keine" setzen (oder in diesem Fall auch belassen).

Sie könnten dasselbe mit etwas tun wie:

def g(a, b, c=None):
    kwargs = {'a': a,
              'b': b}
    if c is not None:
        kwargs['c'] = c

    return f(**kwargs)

Allerdings finde ich das vorige Beispiel trockener und eleganter.

Jocke
quelle
3

Wann ist del in Python nützlich?

Sie können damit ein einzelnes Element eines Arrays anstelle der Slice-Syntax entfernen x[i:i+1]=[]. Dies kann nützlich sein, wenn Sie sich beispielsweise in os.walkeinem Verzeichnis befinden und ein Element im Verzeichnis löschen möchten. Ich würde ein Schlüsselwort dafür jedoch nicht als nützlich erachten, da man einfach eine [].remove(index)Methode erstellen könnte (die .removeMethode ist tatsächlich das Suchen und Entfernen der ersten Wertinstanz).

Ninjagecko
quelle
7
[].pop(index)und [].remove(item). Verwenden Sie keine benannte Variable, "index"wenn Sie über Wert sprechen, da dies verwirrend wirkt.
Ski
@ Ski Pop verwendet Index . Dies ist eine gültige Antwort, während die Hälfte dieser Antworten nur ein Beispiel mit del gibt, bei dem None ebenfalls funktionieren würde. Ein auf None gesetztes Listenobjekt befindet sich noch in der Liste, während del die Elemente entfernt.
user5389726598465
3

Ich habe festgestellt del, dass es für die pseudo-manuelle Speicherverwaltung nützlich ist, wenn große Datenmengen mit Numpy verarbeitet werden. Zum Beispiel:

for image_name in large_image_set:
    large_image = io.imread(image_name)
    height, width, depth = large_image.shape
    large_mask = np.all(large_image == <some_condition>)
    # Clear memory, make space
    del large_image; gc.collect()

    large_processed_image = np.zeros((height, width, depth))
    large_processed_image[large_mask] = (new_value)
    io.imsave("processed_image.png", large_processed_image)

    # Clear memory, make space
    del large_mask, large_processed_image; gc.collect()

Dies kann der Unterschied sein, ob ein Skript zum Stillstand gebracht wird, da das System wie verrückt wechselt, wenn der Python GC nicht mithalten kann, und dass es unterhalb einer losen Speicherschwelle, die viel Spielraum für das Surfen auf dem Computer bietet, einwandfrei funktioniert und Code, während es funktioniert.

Nerv
quelle
2

Ich denke, einer der Gründe, warum del seine eigene Syntax hat, ist, dass das Ersetzen durch eine Funktion in bestimmten Fällen schwierig sein kann, da es sich um die Bindung oder Variable handelt und nicht um den Wert, auf den es verweist. Wenn also eine Funktionsversion von del erstellt werden soll, muss ein Kontext übergeben werden. Del foo muss zu globals (). Remove ('foo') oder local (). Remove ('foo') werden, was chaotisch wird und weniger lesbar. Trotzdem sage ich, Del loszuwerden wäre gut, da es scheinbar selten verwendet wird. Das Entfernen von Sprachmerkmalen / -fehlern kann jedoch schmerzhaft sein. Vielleicht wird Python 4 es entfernen :)

Fuzzy-Waffel
quelle
2

Ich möchte auf die akzeptierte Antwort näher eingehen, um die Nuance zwischen dem Setzen einer Variablen Noneund dem Entfernen mit delfolgenden Variablen hervorzuheben :

Gegeben die Variable foo = 'bar'und die folgende Funktionsdefinition:

def test_var(var):
    if var:
        print('variable tested true')
    else:
        print('variable tested false')

Einmal ursprünglich deklariert, test_var(foo)ergibt sich variable tested truedie erwartete Rendite .

Versuchen Sie jetzt:

foo = None
test_var(foo)

was ergibt variable tested false.

Vergleichen Sie dieses Verhalten mit:

del foo
test_var(foo)

was jetzt erhöht NameError: name 'foo' is not defined.

Jacob
quelle
0

Noch eine Nischenverwendung: In Pyroot mit ROOT5 oder ROOT6 kann "del" nützlich sein, um ein Python-Objekt zu entfernen, das auf ein nicht mehr vorhandenes C ++ - Objekt verweist. Auf diese Weise kann die dynamische Suche von Pyroot ein C ++ - Objekt mit identischem Namen finden und an den Python-Namen binden. Sie können also ein Szenario haben wie:

import ROOT as R
input_file = R.TFile('inputs/___my_file_name___.root')
tree = input_file.Get('r')
tree.Draw('hy>>hh(10,0,5)')
R.gPad.Close()
R.hy # shows that hy is still available. It can even be redrawn at this stage.
tree.Draw('hy>>hh(3,0,3)') # overwrites the C++ object in ROOT's namespace
R.hy # shows that R.hy is None, since the C++ object it pointed to is gone
del R.hy
R.hy # now finds the new C++ object

Hoffentlich wird diese Nische mit dem saneren Objektmanagement von ROOT7 geschlossen.

Amnon Harel
quelle
0

Der Befehl "del" ist sehr nützlich, um Daten in einem Array zu steuern, zum Beispiel:

elements = ["A", "B", "C", "D"]
# Remove first element.
del elements[:1]
print(elements)

Ausgabe:

['B', 'C', 'D']

Victor Marrerp
quelle
-2

Einmal musste ich verwenden:

del serial
serial = None

weil nur verwenden:

serial = None

Die serielle Schnittstelle wurde nicht schnell genug freigegeben, um sie sofort wieder zu öffnen. Aus dieser Lektion habe ich gelernt, dass das delwirklich bedeutete: "GC this NOW! Und warte, bis es fertig ist" und das ist in vielen Situationen wirklich nützlich. Natürlich können Sie eine haben system.gc.del_this_and_wait_balbalbalba(obj).

Alfredolavin
quelle
5
Hmm ... Das hätte eigentlich keinen Unterschied machen sollen. Obwohl Ihr Problem möglicherweise durch die zusätzliche Verzögerung behoben wurde?
Winston Ewert
3
Ich glaube nicht, dass Sie den GC jetzt durch irgendeine Dokumentation unterstützen können. Ich denke, dass __del__()es in Python immer falsch ist , sich auf GC zu verlassen und aufzurufen (ich kenne die Gründe für dieses Design jedoch nicht), und es ist besser, die Kontextmanager-API (die withAnweisung) zu verwenden.
Pavel Šimerda
1
Das ist eine Art Voodoo-Programmierung. Siehe die Methodenbeschreibung und die Notiz in der Dokumentation für das Objekt .__ del __ () für Details, und bedenken Sie nur diese beschreibt CPythons aktuelle Implementierung mit Referenzzählung. Andere Python-Implementierungen (PyPy, Jython, IronPython, Brython usw.) oder zukünftige CPython-Implementierungen verwenden möglicherweise ein anderes Garbage Collection-Schema. Jython verwendet den JVMs GC, der Objekte nicht sofort löscht. Das serialModul funktioniert auch mit Jython, sodass Ihr Hack dort nicht funktioniert!
BlackJack
BTW gc.collect wäre der Weg, um explizit zu recyceln. Wird in den meisten Python-Implementierungen unterstützt.
tdihp
-2

del ist das Äquivalent von "nicht gesetzt" in vielen Sprachen und als Querverweispunkt, der von einer anderen Sprache zu Python wechselt. Menschen neigen dazu, nach Befehlen zu suchen, die das Gleiche tun, was sie in ihrer ersten Sprache getan haben ... auch Einstellungen Eine Variable auf "" oder keine entfernt die Variable nicht wirklich aus dem Gültigkeitsbereich. Sie leert nur ihren Wert. Der Name der Variable selbst wird immer noch im Speicher gespeichert. Warum?!? in einem speicherintensiven Skript ... Müll hinter seinem Nein-Nein und sowieso ... jede Sprache da draußen hat eine Form einer "nicht gesetzten / gelöschten" var-Funktion ... warum nicht Python?

Francisco
quelle
7
delruft den Garbage Collector nicht schneller auf als = Noneund hinterlässt auf lange Sicht auch keinen Müll. Vielleicht möchten Sie die Garbage Collection von Python verbessern.
SilverbackNet
-3

Jedem Objekt in Python ist ein Bezeichner, ein Typ und ein Referenzzähler zugeordnet. Wenn wir del verwenden, wird der Referenzzähler reduziert. Wenn der Referenzzähler Null wird, ist dies ein potenzieller Kandidat für das Sammeln von Müll. Dies unterscheidet das Del im Vergleich zum Setzen eines Bezeichners auf Keine. In einem späteren Fall bedeutet dies einfach, dass das Objekt einfach wild gelassen wird (bis wir außerhalb des Gültigkeitsbereichs sind, in welchem ​​Fall die Anzahl reduziert wird) und der Bezeichner jetzt einfach auf ein anderes Objekt zeigt (Speicherort).

SaiReddy
quelle
3
Ich würde gerne einen Beweis dafür sehen. Das Zuweisen von None sollte den Referenzzähler verringern.
Jason Baker
3
Dies ist ein Unsinn und das Gegenteil von Müllabfuhr (im Sinne, den Müll herumliegen zu lassen).
Pavel Šimerda