Quand est-il utile en python?

375

Je ne peux pas vraiment penser à aucune raison pour laquelle python a besoin du delmot - clé (et la plupart des langues semblent ne pas avoir de mot-clé similaire). Par exemple, plutôt que de supprimer une variable, on pourrait simplement lui affecter None. Et lors de la suppression d'un dictionnaire, une delméthode pourrait être ajoutée.

Y a-t-il une raison de rester delen python, ou est-ce un vestige des jours de pré-collecte des ordures de Python?

Jason Baker
la source
46
Note historique : Python avait la collecte des ordures depuis le début. Avant la version 2.0, le garbage collector de Python ne pouvait pas détecter les cycles de références, mais cela n'avait rien à voir avec del.
Steven Rumbalski
28
@Steven Rumbalksi, cela a quelque chose à voir avec del. Del a été utilisé pour briser les cycles de référence.
Winston Ewert
6
mais deln'est pas un vestige de la pré-collecte des ordures, car vous pourriez toujours l'avoir used = None. Il est toujours logique d'avoir une syntaxe spécifique pour cela. Puisque nous avons maintenant le GC cylindrique, les cas où vous souhaitez utiliser l'un ou l'autre sont petits.
Winston Ewert
3
> et la plupart des langues semblent ne pas avoir de mot-clé similaire. La plupart des langues non récupérées ne le font pas (ou l'utilisent uniquement pour indiquer au GC qu'il peut collecter une variable). La plupart des langues les plus anciennes ont: - De bons vieux langages BASIC tels que QuickBasic avaient ERASE(tuer des variables spécifiques) et CLEAR(tuer toutes les variables)
DrYak

Réponses:

500

Tout d'abord, vous pouvez supprimer d'autres choses en plus des variables locales

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

Les deux devraient être clairement utiles. Deuxièmement, l'utilisation deld'une variable locale rend l'intention plus claire. Comparer:

del foo

à

foo = None

Je sais dans le cas de del foocela que l'intention est de retirer la variable de la portée. Ce n'est pas clair qui foo = Nonefait ça. Si quelqu'un vient d'être assigné, foo = Noneje pourrais penser que c'était du code mort. Mais je sais instantanément ce que quelqu'un qui code del fooessayait de faire.

Winston Ewert
la source
51
+1, oui. Lorsque vous attribuez quelque chose, il exprime l'intention de l'utiliser plus tard. Je ne l'ai vraiment vu delutilisé dans les calculs gourmands en mémoire, mais quand je l'ai vu, j'ai réalisé immédiatement pourquoi c'était nécessaire.
detly
17
Le cas d'utilisation de la suppression d'une liste ou d'un dictionnaire pourrait facilement être remplacé par une méthode (comme je l'ai noté dans la question). Je ne suis pas sûr d' être d' accord avec l'utilisation de delpour signaler l'intention (car un commentaire pourrait faire la même chose sans ajouter au langage), mais je suppose que c'est la meilleure réponse.
Jason Baker
6
@JasonBaker, accordé sur les méthodes. Supprimer des tranches et autres serait cependant plus gênant en utilisant une méthode. Oui, vous pouvez utiliser un commentaire. Mais je pense que l'utilisation d'une déclaration est mieux qu'un commentaire comme partie intégrante du langage.
Winston Ewert
2
@JasonBaker: Il ne s'agit pas seulement de l'intention, ces deux syntaxes font deux choses très différentes.
Pavel Šimerda
2
Il est également géré en le supprimant du dictionnaire respectif. Vous pouvez également contester la len()fonction de la même manière. Si tel est le cas, la question peut avoir été classée comme opinion ou même basée sur le goût. Python tend simplement à fournir des primitives pour les opérations de base au lieu de s'appuyer sur des méthodes.
Pavel Šimerda
161

Il y a cette partie de ce que delfait (à partir de la référence du langage Python ):

La suppression d'un nom supprime la liaison de ce nom de l'espace de noms local ou global

L'affectation Noneà un nom ne supprime pas la liaison du nom de l'espace de noms.

(Je suppose qu'il pourrait y avoir un débat sur la question de savoir si la suppression d'une liaison de nom est réellement utile , mais c'est une autre question.)

Greg Hewgill
la source
22
-1 L'affiche comprend clairement déjà cela, il demande pourquoi vous voulez supprimer une liaison de nom.
Winston Ewert
71
@Winston Ewert: Je ne suis pas sûr que l'affiche comprenne que la delsuppression d'un nom est supprimée car il a suggéré l'attribution Nonecomme alternative.
Steven Rumbalski
8
@Steven, l'affiche contraste clairement la suppression de la variable (suppression du nom) et l'attribution de Aucun. Il ne voit pas pourquoi vous devriez supprimer la variable alors que vous pouvez simplement assigner None. Ils ont le même effet en ce qu'ils libèrent la référence à tout ce qui était auparavant lié à ce nom.
Winston Ewert
19
@Winston Ewert: Ce n'est pas clair pour moi. Peut-être est-il clair pour vous que vous déclarez qu'ils "ont le même effet en ce sens qu'ils libèrent la référence à tout ce qui était auparavant lié à ce nom". Mais ce n'est (clairement?) Pas toute l'histoire dans la mesure où une tentative d'utiliser un nom après sa suppression soulève un NameError. Greg Hewgill fait cette distinction. Et c'est cette distinction qui rend votre affirmation de ce que l'affiche "clairement" comprise peu claire pour moi.
Steven Rumbalski
9
@Winston Ewert Je ne suis pas d'accord. Mais assez dit. Nous avons tous les deux fait valoir nos arguments.
Steven Rumbalski
44

Un endroit que j'ai trouvé delutile est de nettoyer les variables étrangères dans les boucles for:

for x in some_list:
  do(x)
del x

Vous pouvez maintenant être sûr que x ne sera pas défini si vous l'utilisez en dehors de la boucle for.

Jason Baker
la source
1
Votre delligne ici est-elle censée être en retrait (c'est-à-dire une partie de la boucle)?
Sam
11
@Sam, non, ils ne sont pas destinés.
user5319825
7
@Sam Non, l'idée ici est qu'après la dernière itération de la boucle (après avoir effectué do () sur l'élément final de some_list), x resterait une référence à la valeur finale de some_list. del x s'assure que cela ne reste pas assigné en tant que tel.
Matthew
12
Cela entraînera NameError: name 'x' is not definedsi la liste est vide.
WofWca
18

La suppression d'une variable est différente de sa définition sur Aucune

La suppression de noms de variables avec delest probablement quelque chose de rarement utilisé, mais c'est quelque chose qui ne pourrait pas être réalisé sans un mot-clé. Si vous pouvez créer un nom de variable en écrivant a=1, il est bien que vous puissiez théoriquement l'annuler en supprimant a.

Cela peut faciliter le débogage dans certains cas, car essayer d'accéder à une variable supprimée déclenchera une erreur NameError.

Vous pouvez supprimer des attributs d'instance de classe

Python vous permet d'écrire quelque chose comme:

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

Si vous choisissez d'ajouter dynamiquement des attributs à une instance de classe, vous voulez certainement pouvoir l'annuler en écrivant

del a.a
Bernhard
la source
16

Il y a un exemple spécifique de quand vous devez utiliser del(il peut y en avoir d'autres, mais je connais celui-ci à part) lorsque vous utilisez sys.exc_info()pour inspecter une exception. Cette fonction renvoie un tuple, le type d'exception qui a été déclenché, le message et une traceback.

Les deux premières valeurs sont généralement suffisantes pour diagnostiquer une erreur et agir sur celle-ci, mais la troisième contient la pile d'appels entière entre l'endroit où l'exception a été levée et l'endroit où l'exception est interceptée. En particulier, si vous faites quelque chose comme

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

le traceback, tbse retrouve dans les sections locales de la pile d'appels, créant une référence circulaire qui ne peut pas être récupérée. Ainsi, il est important de faire:

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

pour briser la référence circulaire. Dans de nombreux cas où vous voudriez appeler sys.exc_info(), comme avec la magie des métaclasses, le retraçage est utile, vous devez donc vous assurer de le nettoyer avant de pouvoir éventuellement quitter le gestionnaire d'exceptions. Si vous n'avez pas besoin du traçage, vous devez le supprimer immédiatement ou simplement:

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

Pour éviter tout cela ensemble.

SingleNegationElimination
la source
18
Il n'est plus vrai que le garbage collector ne le récupérera pas. Cependant, le cycle retardera la collecte.
Winston Ewert
Ce n'est pas trop pertinent et ne répond pas à la question de l'op.
WhyNotHugo
15

Juste une autre pensée.

Lors du débogage d'applications http dans un framework comme Django, la pile d'appels remplie de variables inutiles et gâchées précédemment utilisées, en particulier lorsqu'il s'agit d'une liste très longue, pourrait être très pénible pour les développeurs. donc, à ce stade, le contrôle de l'espace de noms pourrait être utile.

tdihp
la source
12

L'utilisation explicite de "del" est également une meilleure pratique que l'attribution d'une variable à None. Si vous essayez de supprimer une variable qui n'existe pas, vous obtiendrez une erreur d'exécution mais si vous essayez de définir une variable qui n'existe pas sur None, Python définira silencieusement une nouvelle variable sur None, en laissant la variable que vous voulait supprimé où il était. Alors del vous aidera à rattraper vos erreurs plus tôt

Mat
la source
11

Pour ajouter quelques points aux réponses ci-dessus: del x

La définition de xindique r -> o(une référence rpointant vers un objet o) mais del xchange rplutôt que o. Il s'agit d'une opération sur la référence (pointeur) à l'objet plutôt que sur l'objet associé x. La distinction entre ret oest la clé ici.

  • Il l'enlève locals().
  • Le supprime globals()si xappartient à cet emplacement.
  • Le supprime du cadre de pile (supprime physiquement la référence, mais l'objet lui-même réside dans le pool d'objets et non dans le cadre de pile).
  • Le supprime de la portée actuelle. Il est très utile de limiter la portée de la définition d'une variable locale, ce qui, autrement, peut causer des problèmes.
  • Il s'agit plus de la déclaration du nom que de la définition du contenu.
  • Elle affecte où xappartient, pas où xpointe. Le seul changement physique dans la mémoire est le suivant. Par exemple, si se xtrouve dans un dictionnaire ou une liste, il (comme référence) en est supprimé (et pas nécessairement du pool d'objets). Dans cet exemple, le dictionnaire locals()auquel il appartient est le cadre de pile ( ), qui chevauche globals().
Sohail Si
la source
6

Forcer la fermeture d'un fichier après avoir utilisé numpy.load:

Un créneau d'utilisation peut-être mais je l'ai trouvé utile lors numpy.loadde la lecture d'un fichier. De temps en temps, je mettais à jour le fichier et je devais copier un fichier du même nom dans le répertoire.

J'ai utilisé delpour libérer le fichier et me permettre de copier dans le nouveau fichier.

Remarque Je veux éviter le withgestionnaire de contexte car je jouais avec des tracés sur la ligne de commande et je ne voulais pas appuyer beaucoup sur tab!

Voir cette question.

atomh33ls
la source
J'avais quelque chose de similaire avec des images chargées avec la bibliothèque d'images Python (PIL). J'ouvre une image, et si elle avait certaines dimensions, je voulais supprimer le fichier; cependant, le fichier était toujours utilisé par Python. Par conséquent, j'ai dit «del img», puis j'ai pu supprimer le fichier.
physique du
2
Attention, il s'agit d'un détail d'implémentation car la spécification du langage ne garantit pas quand la __del__()méthode sur les objets poubelle est appelée ou même si elle est appelée du tout. Ainsi, les API qui n'offrent aucun moyen de libérer des ressources autres que d'espérer que la __del__()méthode soit appelée un certain temps (peu de temps après que l'objet est une ordure) sont cassées dans une certaine mesure.
BlackJack
6

delest souvent vu dans les __init__.pyfichiers. Toute variable globale définie dans un __init__.pyfichier est automatiquement "exportée" (elle sera incluse dans a from module import *). Une façon d'éviter cela est de définir __all__, mais cela peut devenir compliqué et tout le monde ne l'utilise pas.

Par exemple, si vous aviez du code __init__.pycomme

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

Ensuite, votre module exporterait le sysnom. Vous devriez plutôt écrire

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

del sys
asmeurer
la source
4

À titre d'exemple de ce qui delpeut être utilisé, je trouve cela utile dans des situations comme celle-ci:

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'

Ces deux fonctions peuvent être dans différents packages / modules et le programmeur n'a pas besoin de savoir quel argument de valeur par défaut cen font fait. Donc, en utilisant kwargs en combinaison avec del, vous pouvez dire "Je veux la valeur par défaut sur c" en la définissant sur None (ou dans ce cas, laissez-la également).

Vous pourriez faire la même chose avec quelque chose comme:

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

    return f(**kwargs)

Cependant je trouve l'exemple précédent plus SEC et plus élégant.

Jocke
la source
3

Quand est-il utile en python?

Vous pouvez l'utiliser pour supprimer un seul élément d'un tableau au lieu de la syntaxe de tranche x[i:i+1]=[]. Cela peut être utile si, par exemple, vous êtes dans os.walket souhaitez supprimer un élément du répertoire. Je ne considérerais pas un mot-clé utile pour cela, car on pourrait simplement créer une [].remove(index)méthode (la .removeméthode est en fait rechercher et supprimer la première instance de valeur).

ninjagecko
la source
7
[].pop(index)et [].remove(item). N'utilisez pas de variable nommée "index"lorsque vous parlez de valeur, cela semble déroutant.
Ski
@Ski pop utilise un index . Ceci est une réponse valide alors que la moitié de ces réponses ne donnent qu'un exemple en utilisant del où None fonctionnerait également. Un objet de liste défini sur Aucun est toujours dans la liste tandis que del supprime les éléments.
user5389726598465
3

J'ai trouvé delutile pour la gestion de la mémoire pseudo-manuelle lors de la manipulation de grandes données avec Numpy. Par exemple:

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

Cela peut être la différence entre l'arrêt brutal d'un script car le système échange comme un fou lorsque le GC Python ne peut pas suivre, et il fonctionne parfaitement en dessous d'un seuil de mémoire lâche qui laisse beaucoup d'espace pour utiliser la machine pour parcourir et le code pendant qu'il fonctionne.

Nerf
la source
2

Je pense que l'une des raisons pour lesquelles del a sa propre syntaxe est que le remplacer par une fonction peut être difficile dans certains cas étant donné qu'il opère sur la liaison ou la variable et non sur la valeur à laquelle il fait référence. Ainsi, si une version de fonction de del devait être créée, un contexte devrait être transmis. Del foo devrait devenir des globaux (). et moins lisible. Je dis toujours que se débarrasser de del serait bien étant donné son utilisation apparemment rare. Mais supprimer les fonctionnalités / défauts du langage peut être douloureux. Peut-être que python 4 le supprimera :)

gaufre floue
la source
2

Je voudrais développer la réponse acceptée pour souligner la nuance entre la définition d'une variable Noneet sa suppression avec del:

Étant donné la variable foo = 'bar'et la définition de fonction suivante:

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

Une fois initialement déclaré, le test_var(foo)rendement variable tested trueest conforme aux attentes.

Maintenant essaye:

foo = None
test_var(foo)

qui cède variable tested false.

Comparez ce comportement avec:

del foo
test_var(foo)

qui soulève maintenant NameError: name 'foo' is not defined.

Jacob
la source
0

Encore une autre utilisation de niche: dans pyroot avec ROOT5 ou ROOT6, "del" peut être utile pour supprimer un objet python qui faisait référence à un objet C ++ qui n'existe plus. Cela permet à la recherche dynamique de pyroot de trouver un objet C ++ portant le même nom et de le lier au nom de python. Vous pouvez donc avoir un scénario tel que:

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

Espérons que ce créneau sera fermé avec la gestion des objets plus saine de ROOT7.

Amnon Harel
la source
0

La commande "del" est très utile pour contrôler les données dans un tableau, par exemple:

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

Production:

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

Victor Marrerp
la source
-2

Une fois, j'ai dû utiliser:

del serial
serial = None

car en utilisant uniquement:

serial = None

n'a pas libéré le port série assez rapidement pour le rouvrir immédiatement. De cette leçon, j'ai appris que delcela signifiait vraiment: "GC this MAINTENANT! Et attendez qu'il soit fait" et c'est vraiment utile dans beaucoup de situations. Bien sûr, vous pouvez en avoir un system.gc.del_this_and_wait_balbalbalba(obj).

Alfredolavin
la source
5
Hmm ... Cela n'aurait vraiment pas dû faire de différence. Bien que votre problème ait peut-être été résolu par le délai supplémentaire qu'il a introduit?
Winston Ewert
3
Je ne pense pas que vous puissiez soutenir le GC maintenant par une quelconque documentation. Je pense que compter sur GC et appeler à __del__()est toujours mauvais en Python (je ne connais pas les raisons de cette conception, cependant) et il est préférable d'utiliser l'API du gestionnaire de contexte (la withdéclaration).
Pavel Šimerda
1
C'est une sorte de programmation vaudou. Voir la description de la méthode et la note dans la documentation de object .__ del __ () pour plus de détails, et gardez à l'esprit que cela ne décrit que l'implémentation actuelle de CPythons avec comptage des références. D'autres implémentations Python (PyPy, Jython, IronPython, Brython,…) ou les futures implémentations CPython peuvent utiliser un schéma de récupération de place différent. Jython utilise le GC JVM qui ne supprime pas immédiatement les objets. Le serialmodule fonctionne également avec Jython afin que votre hack ne fonctionne pas là-bas!
BlackJack
BTW gc.collect serait le moyen de recycler explicitement. Pris en charge dans la plupart des implémentations python.
tdihp
-2

del est l'équivalent de "unset" dans de nombreuses langues et comme point de référence croisé passant d'une autre langue à python .. les gens ont tendance à chercher des commandes qui font la même chose qu'avant dans leur première langue ... un var à "" ou aucun ne supprime pas vraiment le var de la portée .. il vide juste sa valeur le nom du var lui-même serait toujours stocké en mémoire ... pourquoi?!? dans un script gourmand en mémoire..maintenant la corbeille derrière son juste un non non et de toute façon ... chaque langue là-bas a une certaine forme de fonction var "unset / delete "..pourquoi pas python?

Francisco
la source
7
deln'invoque pas le garbage collector plus rapidement que = None, ni ne laissera non plus de déchets à long terme. Vous voudrez peut-être approfondir la collecte des ordures de Python.
SilverbackNet
-3

Chaque objet en python a un identifiant, Type, le nombre de références qui lui est associé, lorsque nous utilisons del le nombre de références est réduit, lorsque le nombre de références devient nul, il est un candidat potentiel pour récupérer les ordures. Cela différencie le del par rapport à la définition d'un identifiant sur Aucun. Dans un cas ultérieur, cela signifie simplement que l'objet est simplement laissé de côté (jusqu'à ce que nous soyons hors de portée, auquel cas le nombre est réduit) et simplement maintenant, l'identifiant pointe vers un autre objet (emplacement de la mémoire).

SaiReddy
la source
3
J'aimerais en voir la preuve. L'affectation de None doit décrémenter le nombre de références.
Jason Baker
3
C'est un non-sens et l'opposé de la collecte des ordures (dans le sens de laisser les ordures traîner).
Pavel Šimerda