web-dev-qa-db-de.com

Wann ist del in Python nützlich?

Ich kann mir keinen Grund vorstellen, warum Python das Schlüsselwort del benötigt (und die meisten Sprachen scheinen kein ähnliches Schlüsselwort zu haben). Anstatt zum Beispiel eine Variable zu löschen, könnte man ihr nur None zuweisen. Beim Löschen aus einem Wörterbuch könnte eine del-Methode hinzugefügt werden.

Gibt es einen Grund, del in Python zu behalten, oder ist es ein Überbleibsel von Pythons Pre-Garbage-Collection-Tagen?

305
Jason Baker

Erstens können Sie andere Dinge als lokale Variablen löschen

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

Beides sollte eindeutig nützlich sein. Zweitens macht die Verwendung von del für eine lokale Variable die Absicht klarer. Vergleichen Sie:

del foo

zu

foo = None

Ich weiß im Fall von del foo, dass die Absicht darin besteht, die Variable aus dem Gültigkeitsbereich zu entfernen. Es ist nicht klar, dass foo = None das tut. Wenn jemand gerade foo = None zugewiesen hat, könnte ich denken, dass es sich um einen toten Code handelt. Aber ich weiß sofort, was jemand versucht, der del foo codiert.

428
Winston Ewert

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

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

Durch die Zuweisung von None zu einem Namen wird die Bindung des Namens nicht aus dem Namespace entfernt.

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

140
Greg Hewgill

Ich habe del als nützlich erachtet, um fremde Variablen in for-Schleifen zu bereinigen:

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. 

35
Jason Baker

Nur ein anderer Gedanke.

Beim Debuggen von HTTP-Anwendungen in einem Framework wie Django kann der Aufrufstapel voller nutzloser und durcheinander geratener Variablen, vor allem wenn es eine sehr lange Liste ist, für Entwickler sehr schmerzhaft sein. Zu diesem Zeitpunkt könnte das Namespace-Controlling nützlich sein.

14
tdihp

Es gibt ein spezifisches Beispiel, wann Sie del verwenden sollten (es gibt möglicherweise noch andere, aber ich kenne diese Möglichkeit), wenn Sie sys.exc_info() verwenden, um eine Ausnahme zu untersuchen. Diese Funktion gibt ein Tupel, den Typ der ausgelösten Ausnahme, die Nachricht und ein Traceback zurück. 

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

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

die Rückverfolgung, tb, endet in den lokalen Werten des Aufrufstapels und erstellt eine Zirkelreferenz, die nicht als Garbage Collection erfasst 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 aufheben. In vielen Fällen, in denen Sie sys.exc_info() aufrufen möchten, wie bei metaclass magic, ist der Traceback nützlich. Sie müssen also sicherstellen, dass Sie ihn bereinigen, bevor Sie den Exception-Handler verlassen können. Wenn Sie das Traceback nicht benötigen, sollten Sie es sofort löschen oder einfach Folgendes tun:

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

Um alles zusammen zu vermeiden. 

Das Löschen einer Variablen unterscheidet sich von der Einstellung auf Keine

Das Löschen von Variablennamen mit del wird wahrscheinlich selten verwendet, was jedoch ohne Schlüsselwort nicht trivial ist. Wenn Sie einen Variablennamen erstellen können, indem Sie a=1 schreiben, ist 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 einfacher werden, da beim Zugriff auf eine gelöschte Variable ein NameError ausgelöst wird.

Sie können Klasseninstanzattribute löschen

In 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 Attribute dynamisch zu einer Klasseninstanz hinzufügen, möchten Sie sie sicherlich durch Schreiben rückgängig machen können

del a.a
13
TheEspinosa

Die explizite Verwendung von "del" ist auch eine bessere Vorgehensweise 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 im Hintergrund eine neue Variable auf None wollte gelöscht werden, wo es war. So hilft del Ihnen, Ihre Fehler früher zu erkennen

8
Matt

So fügen Sie den obigen Antworten einige Punkte hinzu: del x

Die Definition von x gibt r -> o an (eine Referenz r, die auf ein Objekt o zeigt), aber del x ändert r statt o. Es ist eine Operation für den Verweis (Zeiger) auf das Objekt und nicht das mit x verknüpfte Objekt. Die Unterscheidung zwischen r und o ist hier der Schlüssel.

  • Es entfernt es aus locals().
  • Entfernt es aus globals(), wenn x dort hingehört.
  • Entfernt es aus dem Stapelrahmen (entfernt die Referenz physisch daraus, das Objekt selbst befindet sich jedoch im Objektpool und nicht im Stapelrahmen).
  • Entfernt es aus dem aktuellen Bereich. Es ist sehr nützlich, die Definitionsspanne einer lokalen Variablen zu begrenzen, was sonst zu Problemen führen kann.
  • Es geht mehr um die Deklaration des Namens als um die Definition des Inhalts.
  • Es betrifft, wo x gehört, nicht wo x zeigt. Die einzige physische Veränderung im Gedächtnis ist dies. Befindet sich x beispielsweise in einem Wörterbuch oder einer Liste, wird es (als Referenz) von dort entfernt (und nicht unbedingt aus dem Objektpool). In diesem Beispiel ist das zugehörige Wörterbuch der Stack-Frame (locals()), der sich mit globals() überlappt.
7
Sohail Si

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

Eine Nischenverwendung vielleicht, aber ich fand es nützlich, wenn Sie numpy.load verwenden, um eine Datei zu lesen. Hin und wieder würde ich die Datei aktualisieren und eine Datei mit demselben Namen in das Verzeichnis kopieren.

Ich habe del verwendet, um die Datei freizugeben und mich in die neue Datei kopieren zu lassen.

Hinweis: Ich möchte den Kontextmanager with vermeiden, da ich mit Plots in der Befehlszeile herumgespielt habe und nicht viel auf Tab drücken wollte!

Siehe diese Frage.

5
atomh33ls

del wird häufig in __init__.py-Dateien angezeigt. Jede globale Variable, die in einer __init__.py-Datei definiert ist, wird automatisch "exportiert" (sie wird in einem from module import * enthalten). Eine Möglichkeit, dies zu vermeiden, besteht darin, __all__ zu definieren, dies kann jedoch unübersichtlich werden, und nicht jeder verwendet es. 

Zum Beispiel, wenn Sie Code in __init__.py mögen

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

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

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

del sys
5
asmeurer

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

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 sich in unterschiedlichen Paketen/Modulen befinden, und der Programmierer muss nicht wissen, welches Standardwertargument c in f tatsächlich vorhanden ist. Wenn Sie also kwargs in Kombination mit del verwenden, können Sie sagen: "Ich möchte den Standardwert für c", indem Sie den Wert auf None setzen (oder in diesem Fall auch lassen).

Sie könnten dasselbe mit so etwas tun:

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 mehr DRY und elegant.

3
Jocke

Wann ist del in Python nützlich?

Sie können damit ein einzelnes Element eines Arrays anstelle der Slice-Syntax x[i:i+1]=[] entfernen. Dies kann nützlich sein, wenn Sie sich zum Beispiel in os.walk befinden und ein Element im Verzeichnis löschen möchten. Ich würde ein Schlüsselwort jedoch nicht für sinnvoll halten, da man einfach eine [].remove(index)-Methode erstellen könnte (die .remove-Methode ist tatsächlich search-and-remove-first-instance-of-value).

3
ninjagecko

Ich denke, einer der Gründe, aus denen del seine eigene Syntax hat, ist, dass es in bestimmten Fällen schwierig sein kann, sie durch eine Funktion zu ersetzen, wenn sie auf die Bindung oder Variable und nicht auf den Wert, auf den sie verweist, einwirkt. Wenn also eine Funktionsversion von del erstellt werden sollte, müsste ein Kontext übergeben werden. Del foo würde zu globals (). Remove ('foo') oder locals (). Remove ('foo') werden, was unordentlich wird und weniger lesbar. Trotzdem sage ich, del loszuwerden, wäre angesichts seiner scheinbar seltenen Verwendung gut. Das Entfernen von Sprachfeatures/-fehlern kann jedoch schmerzhaft sein. Vielleicht wird Python 4 es entfernen :)

2
fuzzy-waffle

Ich habe festgestellt, dass del 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, wenn Sie ein Skript zum Stillstand bringen, da das System wie verrückt wechselt, wenn der Python GC nicht mehr mithalten kann, und es unter einer losen Speicherschwelle, die viel Headroom lässt, einwandfrei läuft Verwenden des Geräts zum Durchsuchen und Codieren während des Betriebs.

1
Nerve

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

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 der saner object management von ROOT7 geschlossen.

0
Amnon Harel

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']

0
Victor Marrerp