Quando è utile in Python?


375

Non riesco davvero a pensare ad alcun motivo per cui python abbia bisogno della delparola chiave (e la maggior parte delle lingue sembra non avere una parola chiave simile). Ad esempio, anziché eliminare una variabile, si potrebbe semplicemente assegnare Nonead essa. E quando si elimina da un dizionario, delè possibile aggiungere un metodo.

C'è qualche motivo per restare delin Python o è una traccia dei giorni di raccolta dei rifiuti pre-immondizia di Python?


46
Nota storica : Python aveva la raccolta dei rifiuti sin dall'inizio. Prima della 2.0, il garbage collector di Python non era in grado di rilevare cicli di riferimenti, ma non aveva nulla a che fare con del.
Steven Rumbalski

28
@Steven Rumbalksi, ha qualcosa a che fare con del. Del è stato usato per interrompere i cicli di riferimento.
Winston Ewert,

6
ma delnon è una testimonianza della raccolta pre-immondizia perché si potrebbe sempre avere used = None. Ha sempre senso avere una sintassi specifica per questo. Dal momento che ora abbiamo un GC ciclico, i casi in cui si desidera utilizzare uno dei due sono piccoli.
Winston Ewert,

3
> e la maggior parte delle lingue sembra non avere una parola chiave simile La maggior parte delle lingue raccolte con immondizia non lo fanno (o lo usano solo per indicare al GC che può raccogliere una variabile). La maggior parte delle lingue più vecchie ha: - Buone vecchie lingue BASIC come QuickBasic ERASE(uccidere variabili specifiche) e CLEAR(uccidere tutte le variabili)
DrYak

Risposte:


500

Innanzitutto, puoi eliminare altre cose oltre alle variabili locali

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

Entrambi dovrebbero essere chiaramente utili. In secondo luogo, l'utilizzo delsu una variabile locale rende più chiaro l'intento. Confrontare:

del foo

per

foo = None

So che in questo caso del fool'intento è rimuovere la variabile dall'ambito. Non è chiaro che lo foo = Nonestia facendo. Se qualcuno appena assegnato foo = Nonepotrei pensare che fosse un codice morto. Ma so immediatamente cosa del foosta cercando di fare qualcuno che sta programmando.


51
+1, sì. Quando assegni qualcosa, si trasmette l'intenzione di usarlo in seguito. L'ho visto solo delusato nei calcoli ad alta intensità di memoria, ma quando l'ho visto ho capito immediatamente perché fosse necessario.
detly

17
Il caso d'uso dell'eliminazione da un elenco o dizionario potrebbe essere facilmente sostituito con un metodo (come ho notato nella domanda). Non sono sicuro di essere d'accordo con l'uso di delper segnalare l'intento (poiché un commento potrebbe fare lo stesso senza aggiungere alla lingua), ma suppongo che sia la risposta migliore.
Jason Baker,

6
@JasonBaker, concesso sui metodi. L'eliminazione di porzioni e simili sarebbe più imbarazzante usando un metodo però. Sì, potresti usare un commento. Ma penso che usare un'istruzione sia meglio di un commento come parte della lingua.
Winston Ewert,

2
@JasonBaker: non si tratta solo dell'intento, quelle due sintassi fanno due cose molto diverse.
Pavel Šimerda,

2
Viene anche gestito eliminandolo dal rispettivo dizionario. Potresti anche contestare la len()funzione allo stesso modo. In tal caso, la domanda potrebbe essere stata chiusa come opinione o addirittura basata sul gusto. Python tende semplicemente a fornire primitive per le operazioni di base invece di fare affidamento sui metodi.
Pavel Šimerda,

161

C'è questa parte di ciò che delfa (dal Python Language Reference ):

La cancellazione di un nome rimuove l'associazione di quel nome dallo spazio dei nomi locale o globale

L'assegnazione Nonea un nome non rimuove l'associazione del nome dallo spazio dei nomi.

(Suppongo che ci potrebbe essere un dibattito sul fatto che la rimozione di un nome associato sia effettivamente utile , ma questa è un'altra domanda.)


22
-1 Il poster chiaramente lo capisce già, sta chiedendo perché vuoi rimuovere un nome associato.
Winston Ewert,

71
@Winston Ewert: Non sono sicuro che il poster abbia capito che delrimuove un nome associato come ha suggerito di assegnare Nonein alternativa.
Steven Rumbalski

8
@Steven, il poster contrasta chiaramente eliminando la variabile (rimuovendo il nome) e assegnando None. Non capisce perché dovresti eliminare la variabile quando puoi semplicemente assegnare None. Hanno lo stesso effetto in quanto rilasciano il riferimento a qualunque cosa fosse precedentemente legata a quel nome.
Winston Ewert,

19
@ Winston Ewert: non mi è chiaro. Forse ti è chiaro in quanto affermi che "hanno lo stesso effetto in quanto il rilascio del riferimento a qualunque cosa fosse precedentemente legata a quel nome". Ma questa (chiaramente?) Non è l'intera storia in quanto un tentativo di usare un nome dopo averlo cancellato genera un NameError. Greg Hewgill fa questa distinzione. Ed è questa distinzione che rende poco chiara la tua affermazione di ciò che il poster "chiaramente" ha capito.
Steven Rumbalski

9
@ Winston Ewert Non sono d'accordo. Ma abbastanza detto. Entrambi abbiamo realizzato i nostri casi.
Steven Rumbalski,

44

Un posto che ho trovato delutile è ripulire le variabili estranee per i loop:

for x in some_list:
  do(x)
del x

Ora puoi essere sicuro che x sarà indefinito se lo usi al di fuori del ciclo for.


1
La tua dellinea qui dovrebbe essere rientrata (cioè parte del ciclo)?
Sam,

11
@Sam, No, non sono previsti.
user5319825

7
@Sam No, l'idea qui è che dopo l'ultima iterazione del loop (dopo aver eseguito do () sull'elemento finale di some_list), x rimarrebbe un riferimento al valore finale di some_list. del x si assicura che questo non rimanga assegnato come tale.
Matteo,

12
Ciò causerà NameError: name 'x' is not definedse l'elenco è vuoto.
WofWca,

18

L'eliminazione di una variabile è diversa dall'impostazione su Nessuno

L'eliminazione di nomi di variabili con delè probabilmente qualcosa di usato raramente, ma è qualcosa che non potrebbe essere ottenuto banalmente senza una parola chiave. Se riesci a creare un nome variabile scrivendo a=1, è bello poter teoricamente annullarlo cancellando a.

In alcuni casi può semplificare il debug poiché il tentativo di accedere a una variabile eliminata genererà un NameError.

È possibile eliminare gli attributi dell'istanza di classe

Python ti consente di scrivere qualcosa del tipo:

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

Se scegli di aggiungere dinamicamente attributi a un'istanza di classe, sicuramente vorrai essere in grado di annullarlo scrivendo

del a.a

16

C'è un esempio specifico di quando dovresti usare del(potrebbero essercene altri, ma ne conosco uno fuori mano) quando stai usando sys.exc_info()per ispezionare un'eccezione. Questa funzione restituisce una tupla, il tipo di eccezione sollevata, il messaggio e un traceback.

I primi due valori sono in genere sufficienti per diagnosticare un errore e agire su di esso, ma il terzo contiene l'intero stack di chiamate tra dove è stata sollevata l'eccezione e dove viene rilevata l'eccezione. In particolare, se fai qualcosa del genere

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

il traceback, tbfinisce nei locali dello stack di chiamate, creando un riferimento circolare che non può essere garbage collection. Pertanto, è importante fare:

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

per rompere il riferimento circolare. In molti casi in cui vorresti chiamare sys.exc_info(), come con la metaclasse, il traceback è utile, quindi devi assicurarti di ripulirlo prima di poter eventualmente lasciare il gestore delle eccezioni. Se non è necessario il traceback, è necessario eliminarlo immediatamente o semplicemente:

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

Per evitare tutto insieme.


18
Non è più vero che il garbage collector non lo raccoglierà. Tuttavia, il ciclo ritarderà la raccolta.
Winston Ewert,

Questo non è troppo rilevante e non risponde alla domanda dell'op.
WhyNotHugo,

15

Solo un altro pensiero.

Quando si esegue il debug di applicazioni http in framework come Django, lo stack di chiamate pieno di variabili inutili e incasinate precedentemente utilizzate, specialmente quando è un elenco molto lungo, potrebbe essere molto doloroso per gli sviluppatori. quindi, a questo punto, il controllo dello spazio dei nomi potrebbe essere utile.


12

L'uso esplicito di "del" è anche una pratica migliore rispetto all'assegnazione di una variabile a None. Se si tenta di eliminare una variabile che non esiste, si otterrà un errore di runtime ma se si tenta di impostare una variabile che non esiste su None, Python imposterà silenziosamente una nuova variabile su None, lasciando la variabile voleva essere eliminato dov'era. Quindi del ti aiuterà a rilevare i tuoi errori prima


11

Per aggiungere alcuni punti alle risposte sopra: del x

La definizione di xindica r -> o(un riferimento che rpunta a un oggetto o) ma del xcambia ranziché o. È un'operazione sul riferimento (puntatore) all'oggetto anziché all'oggetto associato x. Distinguere tra red oè la chiave qui.

  • Lo rimuove da locals().
  • Rimuove da globals()se xappartiene lì.
  • Rimuove dal frame dello stack (rimuove fisicamente il riferimento da esso, ma l'oggetto stesso risiede nel pool di oggetti e non nel frame dello stack).
  • Rimuove dall'ambito corrente. È molto utile limitare l'intervallo di definizione di una variabile locale, che altrimenti può causare problemi.
  • Si tratta più della dichiarazione del nome piuttosto che della definizione del contenuto.
  • Colpisce dove xappartiene, non dove xpunta. L'unico cambiamento fisico nella memoria è questo. Ad esempio, se si xtrova in un dizionario o in un elenco, viene rimosso (come riferimento) da lì (e non necessariamente dal pool di oggetti). In questo esempio, il dizionario a cui appartiene è lo stack frame ( locals()), che si sovrappone a globals().

6

Forzare la chiusura di un file dopo aver utilizzato numpy.load:

Un utilizzo di nicchia forse ma l'ho trovato utile quando si utilizzava numpy.loadper leggere un file. Ogni tanto aggiornavo il file e dovevo copiare un file con lo stesso nome nella directory.

Ho usato delper rilasciare il file e mi permette di copiare nel nuovo file.

Nota Voglio evitare il withgestore del contesto mentre stavo giocando con grafici sulla riga di comando e non volevo premere molto la linguetta!

Vedi questa domanda


Ho avuto qualcosa di simile con le immagini caricate con la libreria di immagini Python (PIL). Apro un'immagine e, se avesse determinate dimensioni, volevo cancellare il file; tuttavia il file era ancora in uso da Python. Pertanto, ho detto 'del img', e quindi ho potuto rimuovere il file.
fisica

2
Attenzione che si tratta di un dettaglio di implementazione poiché la specifica del linguaggio non garantisce quando __del__()viene chiamato il metodo su oggetti garbage o anche se viene chiamato affatto. Quindi le API che non offrono alcun modo per rilasciare risorse diverse dalla speranza che il __del__()metodo venga chiamato un po 'di tempo (subito dopo che l'oggetto è spazzatura) vengono in qualche modo interrotte.
BlackJack,

6

delè spesso visto nei __init__.pyfile. Qualsiasi variabile globale definita in un __init__.pyfile viene automaticamente "esportata" (verrà inclusa in a from module import *). Un modo per evitarlo è definire __all__, ma questo può diventare confuso e non tutti lo usano.

Ad esempio, se avevi il codice in __init__.pylike

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

Quindi il tuo modulo dovrebbe esportare il sysnome. Dovresti invece scrivere

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

del sys

4

Come esempio di ciò che delpuò essere utilizzato, lo trovo utile in situazioni come questa:

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'

Queste due funzioni possono essere in diversi pacchetti / moduli e il programmatore non ha bisogno di sapere quale valore di default argomento cin frealtà hanno. Quindi usando kwargs in combinazione con del puoi dire "Voglio il valore predefinito su c" impostandolo su Nessuno (o in questo caso anche lasciarlo).

Potresti fare la stessa cosa con qualcosa del tipo:

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

    return f(**kwargs)

Comunque trovo l'esempio precedente più ASCIUTTO ed elegante.


3

Quando è utile in Python?

È possibile utilizzarlo per rimuovere un singolo elemento di un array anziché la sintassi della sezione x[i:i+1]=[]. Ciò può essere utile se, ad esempio, ci si trova os.walke si desidera eliminare un elemento nella directory. Non considererei utile una parola chiave per questo, dal momento che si potrebbe semplicemente creare un [].remove(index)metodo (il .removemetodo è in realtà cerca-e-rimuovi-prima-istanza-di-valore).


7
[].pop(index)e [].remove(item). Non usare la variabile nominata "index"quando si parla di valore, la rende confusa.
Sci,

@Ski pop usa l' indice . Questa è una risposta valida mentre la metà di queste risposte fornisce solo un esempio usando del dove None funzionerebbe anche. Un oggetto elenco impostato su Nessuno è ancora nell'elenco mentre del rimuove gli elementi.
user5389726598465

3

Ho trovato delutile per la gestione della memoria pseudo-manuale quando si gestiscono dati di grandi dimensioni con Numpy. Per esempio:

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()

Questa può essere la differenza tra interrompere uno script mentre il sistema si scambia come un matto quando il Python GC non riesce a tenere il passo, ed è perfettamente scorrevole sotto una soglia di memoria libera che lascia molto spazio per usare la macchina per navigare e codice mentre funziona.


2

Penso che uno dei motivi per cui del abbia una sua sintassi sia che in alcuni casi sostituirlo con una funzione potrebbe essere difficile dato che opera sul legame o sulla variabile e non sul valore a cui fa riferimento. Pertanto, se si dovesse creare una versione della funzione di del, sarebbe necessario passare un contesto. Del foo dovrebbe diventare globals (). Remove ('foo') o locals (). Remove ('foo') che diventa disordinato e meno leggibile. Comunque dico che sbarazzarsi di del sarebbe buono dato il suo uso apparentemente raro. Ma rimuovere funzionalità / difetti del linguaggio può essere doloroso. Forse Python 4 lo rimuoverà :)


2

Vorrei approfondire la risposta accettata per evidenziare la sfumatura tra l'impostazione di una variabile Nonee la sua rimozione con del:

Data la variabile foo = 'bar'e la seguente definizione di funzione:

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

Una volta dichiarato inizialmente, i test_var(foo)rendimenti sono variable tested trueattesi.

Ora prova:

foo = None
test_var(foo)

quale cede variable tested false.

Contrasta questo comportamento con:

del foo
test_var(foo)

che ora aumenta NameError: name 'foo' is not defined.


0

Ancora un altro utilizzo di nicchia: in pyroot con ROOT5 o ROOT6, "del" può essere utile per rimuovere un oggetto Python che faceva riferimento a un oggetto C ++ non più esistente. Ciò consente alla ricerca dinamica di pyroot di trovare un oggetto C ++ con nome identico e associarlo al nome Python. Quindi puoi avere uno scenario come:

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

Si spera che questa nicchia verrà chiusa con la gestione degli oggetti saner di ROOT7.


0

Il comando "del" è molto utile per controllare i dati in un array, ad esempio:

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

Produzione:

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


-2

Una volta ho dovuto usare:

del serial
serial = None

perché usando solo:

serial = None

non ha rilasciato la porta seriale abbastanza velocemente da riaprirla immediatamente. Da quella lezione ho imparato che delsignificava davvero: "GC questo ADESSO! E attendi che sia fatto" e questo è davvero utile in molte situazioni. Certo, potresti avere un system.gc.del_this_and_wait_balbalbalba(obj).


5
Hmm ... Questo non avrebbe dovuto fare la differenza. Tuttavia, forse il tuo problema è stato risolto dal ritardo aggiuntivo che ha introdotto?
Winston Ewert,

3
Non credo che tu possa appoggiare il GC ora con qualsiasi documentazione. Penso che fare affidamento su GC e chiamare a __del__()sia sempre sbagliato in Python (non conosco i motivi di questo progetto) ed è meglio usare l'API del gestore del contesto (la withdichiarazione).
Pavel Šimerda,

1
È una sorta di programmazione voodoo. Vedere la descrizione del metodo e la nota nella documentazione per l'oggetto .__ del __ () per i dettagli e tenere presente che ciò descrive solo l'implementazione corrente di CPythons con il conteggio dei riferimenti. Altre implementazioni di Python (PyPy, Jython, IronPython, Brython, ...) o future implementazioni di CPython potrebbero utilizzare un diverso schema di garbage collection. Jython utilizza il GC JVMs che non cancella immediatamente gli oggetti. Il serialmodulo funziona anche con Jython, quindi il tuo hack non funziona lì!
BlackJack,

BTW gc.collect sarebbe il modo di riciclare esplicitamente. Supportato nella maggior parte delle implementazioni di Python.
tdihp,

-2

del è l'equivalente di "unset" in molte lingue e come punto di riferimento incrociato passando da un'altra lingua a Python .. le persone tendono a cercare comandi che fanno la stessa cosa che facevano nella loro prima lingua ... anche impostando una var a "" o nessuna non rimuove realmente la var dall'ambito .. svuota semplicemente il suo valore, il nome della var stessa verrebbe comunque memorizzato ... perché?!? in uno script ad alta intensità di memoria..mantenere spazzatura dietro il suo solo un no no e comunque ... ogni lingua là fuori ha una qualche forma di una funzione var "unset / delete "..perché non python?


7
delnon invoca il garbage collector più rapidamente di = None, né lascerà indietro la spazzatura a lungo termine. Potresti voler dare un'occhiata alla raccolta dei rifiuti di Python.
SilverbackNet,

-3

Ogni oggetto in Python ha un identificatore, Tipo, conteggio dei riferimenti associato ad esso, quando usiamo il conteggio dei riferimenti viene ridotto, quando il conteggio dei riferimenti diventa zero è un potenziale candidato per la raccolta dei rifiuti. Questo differenzia il del rispetto all'impostazione di un identificatore su Nessuno. In un secondo momento significa semplicemente che l'oggetto viene semplicemente lasciato fuori (fino a quando non siamo fuori campo nel qual caso il conteggio viene ridotto) e semplicemente ora l'identificatore punta a qualche altro oggetto (posizione di memoria).


3
Mi piacerebbe vedere la prova di questo. L'assegnazione di Nessuno dovrebbe ridurre il conteggio dei riferimenti.
Jason Baker,

3
Questa è un'assurdità e l'opposto della raccolta dei rifiuti (nel senso di lasciare la spazzatura in giro).
Pavel Šimerda,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.