¿Cuándo es del útil en python?

375

Realmente no puedo pensar en ninguna razón por la cual Python necesita la delpalabra clave (y la mayoría de los idiomas parecen no tener una palabra clave similar). Por ejemplo, en lugar de eliminar una variable, uno simplemente podría asignarle None. Y al eliminar de un diccionario, delse puede agregar un método.

¿Hay alguna razón para quedarse delen Python, o es un vestigio de los días previos a la recolección de basura de Python?

Jason Baker
fuente
46
Nota histórica : Python tuvo recolección de basura desde el principio. Antes de 2.0, el recolector de basura de Python no podía detectar ciclos de referencias, pero eso no tenía nada que ver del.
Steven Rumbalski
28
@ Steven Rumbalksi, tiene algo que ver con del. Del se usó para romper los ciclos de referencia.
Winston Ewert
66
pero delno es un vestigio de la recolección previa a la basura porque siempre podría haberlo hecho used = None. Siempre tiene sentido tener una sintaxis específica para ello. Dado que ahora tenemos GC cilíndrico, los casos en los que desea usar cualquiera de los dos son pequeños.
Winston Ewert
3
> y la mayoría de los idiomas parecen no tener una palabra clave similar La mayoría de los idiomas recolectados no tienen (o solo lo usan para indicarle al GC que puede recolectar una variable). La mayoría de los idiomas más antiguos sí: - Buenos y viejos lenguajes BÁSICOS como QuickBasic ERASE(eliminar variables específicas) y CLEAR(eliminar todas las variables)
DrYak

Respuestas:

500

En primer lugar, puede eliminar otras cosas además de las variables locales.

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

Ambos deberían ser claramente útiles. En segundo lugar, el uso delde una variable local aclara la intención. Comparar:

del foo

a

foo = None

Sé en el caso de del fooque la intención es eliminar la variable del alcance. No está claro que eso foo = Noneesté haciendo. Si alguien acaba de asignar foo = None, podría pensar que fue un código muerto. Pero instantáneamente sé lo que alguien que codifica del fooestaba tratando de hacer.

Winston Ewert
fuente
51
+1, si. Cuando asigna algo, transmite la intención de usarlo más tarde. Realmente solo lo he visto delusado en cálculos intensivos en memoria, pero cuando lo vi me di cuenta de inmediato por qué era necesario.
detly
17
El caso de uso de eliminar de una lista o diccionario podría reemplazarse fácilmente con un método (como señalé en la pregunta). No estoy seguro de estar de acuerdo con el uso de delpara señalar la intención (ya que un comentario podría hacer lo mismo sin agregar al lenguaje), pero supongo que esa es la mejor respuesta.
Jason Baker,
66
@JasonBaker, otorgado sobre los métodos. Sin embargo, eliminar partes y cosas similares sería más incómodo utilizando un método. Sí, podrías usar un comentario. Pero creo que usar una declaración es mejor que un comentario como parte del lenguaje.
Winston Ewert
2
@JasonBaker: No se trata solo de la intención, esas dos sintaxis hacen dos cosas muy diferentes.
Pavel Šimerda
2
También se maneja eliminándolo del diccionario respectivo. También podría disputar la len()función de la misma manera. Si este es el caso, la pregunta puede haberse cerrado como opinión o incluso basada en el gusto. Python simplemente tiende a proporcionar primitivas para operaciones básicas en lugar de depender de métodos.
Pavel Šimerda
161

Hay esta parte de lo que delhace (de la Referencia del lenguaje Python ):

La eliminación de un nombre elimina el enlace de ese nombre del espacio de nombres local o global

Asignar Nonea un nombre no elimina el enlace del nombre del espacio de nombres.

(Supongo que podría haber algún debate sobre si eliminar un enlace de nombre es realmente útil , pero esa es otra pregunta).

Greg Hewgill
fuente
22
-1 El póster claramente ya entiende esto, está preguntando por qué quieres eliminar un enlace de nombre.
Winston Ewert
71
@ Winston Ewert: No estoy seguro de que el póster entendiera que delelimina un enlace de nombre, ya que sugirió asignar Nonecomo alternativa.
Steven Rumbalski
8
@ Steven, el póster contrasta claramente al eliminar la variable (eliminar el nombre) y asignar Ninguno. No ve por qué debería eliminar la variable cuando solo puede asignar Ninguno. Tienen el mismo efecto, ya que liberan la referencia a lo que anteriormente estaba vinculado a ese nombre.
Winston Ewert
19
@ Winston Ewert: No está claro para mí. Quizás tenga claro que usted afirma que "tienen el mismo efecto que la publicación de la referencia a lo que anteriormente estaba vinculado a ese nombre". Pero esa (¿está claro?) No es toda la historia, ya que un intento de usar un nombre después de eliminarlo plantea a NameError. Greg Hewgill hace esta distinción. Y es esta distinción la que hace que su afirmación de lo que el cartel "claramente" entendió no me resulta clara.
Steven Rumbalski
99
@ Winston Ewert No estoy de acuerdo. Pero suficiente dicho. Ambos hemos presentado nuestros casos.
Steven Rumbalski
44

Un lugar que he encontrado delútil es limpiar variables extrañas en bucles for:

for x in some_list:
  do(x)
del x

Ahora puede estar seguro de que x estará indefinido si lo usa fuera del ciclo for.

Jason Baker
fuente
1
¿Se delsupone que su línea aquí tiene sangría (es decir, parte del bucle)?
Sam
11
@ Sam, no, no están destinados.
user5319825
77
@Sam No, la idea aquí es que después de la última iteración del bucle (después de realizar do () en el elemento final de some_list), x seguiría siendo una referencia al valor final de some_list. del x se asegura de que esto no permanezca asignado como tal.
Mateo
12
Esto causará NameError: name 'x' is not definedsi la lista está vacía.
WofWca
18

Eliminar una variable es diferente a establecerla en Ninguno

Eliminar nombres de variables con deles probablemente algo que se usa raramente, pero es algo que no podría lograrse trivialmente sin una palabra clave. Si puede crear un nombre de variable escribiendo a=1, es bueno que, en teoría, pueda deshacerlo eliminando a.

Puede facilitar la depuración en algunos casos, ya que intentar acceder a una variable eliminada generará un NameError.

Puede eliminar atributos de instancia de clase

Python te permite escribir algo como:

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

Si elige agregar atributos dinámicamente a una instancia de clase, ciertamente desea poder deshacerlo escribiendo

del a.a
Bernhard
fuente
16

Hay un ejemplo específico de cuándo debe usar del(puede haber otros, pero sé de esto de antemano) cuando está usando sys.exc_info()para inspeccionar una excepción. Esta función devuelve una tupla, el tipo de excepción que se generó, el mensaje y un rastreo.

Los dos primeros valores suelen ser suficientes para diagnosticar un error y actuar en consecuencia, pero el tercero contiene toda la pila de llamadas entre el lugar donde se produjo la excepción y donde se detecta la excepción. En particular, si haces algo como

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

el rastreo, tbtermina en los locales de la pila de llamadas, creando una referencia circular que no se puede recolectar basura. Por lo tanto, es importante hacer:

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

para romper la referencia circular. En muchos casos en los que desea llamar sys.exc_info(), como con la metaclase mágica, el rastreo es útil, por lo que debe asegurarse de limpiarlo antes de que pueda dejar el controlador de excepciones. Si no necesita el rastreo, debe eliminarlo de inmediato, o simplemente hacer:

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

Para evitarlo todo junto.

SingleNegationElimination
fuente
18
Ya no es cierto que el recolector de basura no lo recogerá. Sin embargo, el ciclo retrasará la recolección.
Winston Ewert
Esto no es demasiado relevante y no responde la pregunta del operador.
WhyNotHugo
15

Solo otro pensamiento.

Al depurar aplicaciones http en framework como Django, la pila de llamadas llena de variables inútiles y en mal estado utilizadas anteriormente, especialmente cuando es una lista muy larga, podría ser muy dolorosa para los desarrolladores. entonces, en este punto, el control del espacio de nombres podría ser útil.

tdihp
fuente
12

Usar "del" explícitamente también es una mejor práctica que asignar una variable a None. Si intenta eliminar una variable que no existe, obtendrá un error de tiempo de ejecución, pero si intenta establecer una variable que no existe en None, Python establecerá silenciosamente una nueva variable en None, dejando la variable quería borrado donde estaba. Así que del te ayudará a detectar tus errores antes

Mate
fuente
11

Para agregar algunos puntos a las respuestas anteriores: del x

La definición de xindica r -> o(una referencia que rapunta a un objeto o) pero del xcambia en rlugar de o. Es una operación en la referencia (puntero) para objetar en lugar del objeto asociado x. Distinguir entre ry oes clave aquí.

  • Se lo quita locals().
  • Lo elimina de globals()si xpertenece allí.
  • Lo elimina del marco de la pila (elimina físicamente la referencia, pero el objeto mismo reside en el grupo de objetos y no en el marco de la pila).
  • Lo elimina del alcance actual. Es muy útil limitar el alcance de la definición de una variable local, que de lo contrario puede causar problemas.
  • Se trata más de la declaración del nombre que de la definición del contenido.
  • Afecta a dónde xpertenece, no a dónde xapunta. El único cambio físico en la memoria es este. Por ejemplo, si xestá en un diccionario o lista, se elimina (como referencia) de allí (y no necesariamente del grupo de objetos). En este ejemplo, el diccionario al que pertenece es el marco de pila ( locals()), que se superpone con globals().
Sohail Si
fuente
6

Forzar el cierre de un archivo después de usar numpy.load:

Tal vez sea un nicho de uso, pero lo encontré útil al usar numpy.loadpara leer un archivo. De vez en cuando, actualizaba el archivo y necesitaba copiar un archivo con el mismo nombre en el directorio.

Solía delliberar el archivo y permitirme copiar en el nuevo archivo.

Tenga en cuenta que quiero evitar el withadministrador de contexto ya que estaba jugando con tramas en la línea de comando y no quería presionar mucho la pestaña.

Ver esta pregunta

atomh33ls
fuente
Tenía algo similar con las imágenes cargadas con la Biblioteca de imágenes de Python (PIL). Abro una imagen y, si tenía ciertas dimensiones, quería eliminar el archivo; sin embargo, el archivo todavía estaba en uso por Python. Por lo tanto, dije 'del img', y luego pude eliminar el archivo.
fisicalattraction
2
Tenga en cuenta que este es un detalle de implementación ya que la especificación del lenguaje no garantiza cuándo __del__()se llama al método en los objetos basura o incluso si se llama en absoluto. Por lo tanto, las API que no ofrecen ninguna forma de liberar recursos más que esperar que el __del__()método se llame algún tiempo (poco después de que el objeto sea basura) se rompen hasta cierto punto.
BlackJack
6

delA menudo se ve en los __init__.pyarchivos. Cualquier variable global que se define en un __init__.pyarchivo se "exporta" automáticamente (se incluirá en a from module import *). Una forma de evitar esto es definir __all__, pero esto puede ser complicado y no todos lo usan.

Por ejemplo, si tuviera código __init__.pycomo

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

Entonces su módulo exportaría el sysnombre. En su lugar deberías escribir

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

del sys
asmeurer
fuente
4

Como ejemplo de lo que delse puede usar, me resulta útil en situaciones como esta:

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'

Estas dos funciones pueden estar en diferentes paquetes / módulos y el programador no necesita saber qué argumento valor predeterminado cen frealidad tienen. Entonces, al usar kwargs en combinación con del, puede decir "Quiero el valor predeterminado en c" configurándolo en Ninguno (o en este caso también dejarlo).

Podrías hacer lo mismo con algo como:

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

    return f(**kwargs)

Sin embargo, el ejemplo anterior me parece más SECO y elegante.

Jocke
fuente
3

¿Cuándo es del útil en python?

Puede usarlo para eliminar un solo elemento de una matriz en lugar de la sintaxis de corte x[i:i+1]=[]. Esto puede ser útil si, por ejemplo, está en os.walky desea eliminar un elemento en el directorio. Sin embargo, no consideraría que una palabra clave sea útil para esto, ya que uno podría hacer un [].remove(index)método (el .removemétodo es en realidad buscar y eliminar la primera instancia de valor).

ninjagecko
fuente
77
[].pop(index)y [].remove(item). No use la variable nombrada "index"cuando se habla de valor, lo hace parecer confuso.
Ski
@Ski pop usa el índice . Esta es una respuesta válida, mientras que la mitad de estas respuestas solo dan un ejemplo usando del donde None también funcionaría. Un objeto de lista establecido en Ninguno todavía está en la lista, mientras que del elimina los elementos.
user5389726598465
3

He encontrado delque es útil para la gestión de memoria pseudo-manual cuando se manejan datos grandes con Numpy. Por ejemplo:

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

Esta puede ser la diferencia entre detener un script cuando el sistema cambia como loco cuando el Python GC no puede seguir el ritmo, y se ejecuta perfectamente por debajo de un umbral de memoria suelto que deja mucho margen para usar la máquina para navegar y codificar mientras funciona.

Nervio
fuente
2

Creo que una de las razones por las que del tiene su propia sintaxis es que reemplazarla con una función podría ser difícil en ciertos casos dado que opera en el enlace o variable y no en el valor al que hace referencia. Por lo tanto, si se creara una versión de función de del, se tendría que pasar un contexto. y menos legible. Todavía digo que deshacerse de Del sería bueno dado su uso aparentemente raro. Pero eliminar las características / defectos del lenguaje puede ser doloroso. Tal vez Python 4 lo eliminará :)

gofre borroso
fuente
2

Me gustaría profundizar en la respuesta aceptada para resaltar el matiz entre establecer una variable en lugar de Noneeliminarla con del:

Dada la variable foo = 'bar', y la siguiente definición de función:

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

Una vez declarado inicialmente, test_var(foo)rinde variable tested truecomo se esperaba.

Ahora intenta:

foo = None
test_var(foo)

que rinde variable tested false.

Contraste este comportamiento con:

del foo
test_var(foo)

que ahora se levanta NameError: name 'foo' is not defined.

jacob
fuente
0

Otro uso de nicho: en pyroot con ROOT5 o ROOT6, "del" puede ser útil para eliminar un objeto de python que hace referencia a un objeto C ++ que ya no existe. Esto permite que la búsqueda dinámica de pyroot encuentre un objeto C ++ con un nombre idéntico y lo una al nombre de python. Entonces puede tener un escenario como:

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

Con suerte, este nicho se cerrará con la gestión de objetos más sensata de ROOT7.

Amnon Harel
fuente
0

El comando "del" es muy útil para controlar datos en una matriz, por ejemplo:

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

Salida:

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

Victor Marrerp
fuente
-2

Una vez que tuve que usar:

del serial
serial = None

porque usando solo:

serial = None

no liberó el puerto serie lo suficientemente rápido como para abrirlo nuevamente de inmediato. De esa lección aprendí que delrealmente significaba: "¡GC esto AHORA! Y espera hasta que termine" y eso es realmente útil en muchas situaciones. Por supuesto, puede tener un system.gc.del_this_and_wait_balbalbalba(obj).

alfredolavin
fuente
55
Hmm ... Eso realmente no debería haber hecho una diferencia. Aunque, ¿tal vez su problema se solucionó por el retraso adicional que introdujo?
Winston Ewert
3
No creo que pueda respaldar el GC ahora con ninguna documentación. Creo que confiar en GC y llamar __del__()siempre está mal en Python (sin embargo, no sé los motivos de este diseño) y es mejor usar la API de administrador de contexto (la withdeclaración).
Pavel Šimerda
1
Esa es una especie de programación vudú. Consulte la descripción del método y la nota en la documentación del objeto .__ del __ () para obtener detalles, y tenga en cuenta que esto solo describe la implementación actual de CPythons con recuento de referencias. Otras implementaciones de Python (PyPy, Jython, IronPython, Brython, ...) o futuras implementaciones de CPython pueden usar un esquema de recolección de basura diferente. Jython utiliza el GC JVM que no elimina objetos de inmediato. ¡El serialmódulo también funciona con Jython, por lo que su truco no funciona allí!
BlackJack
Por cierto, gc.collect sería la forma de reciclar explícitamente. Compatible con la mayoría de las implementaciones de Python.
tdihp
-2

del es el equivalente de "desarmado" en muchos idiomas y como punto de referencia cruzada al pasar de otro idioma a python ... las personas tienden a buscar comandos que hacen lo mismo que solían hacer en su primer idioma ... también configurando una var a "" o none realmente no elimina la var del alcance ... solo vacía su valor, el nombre de la var en sí misma todavía se almacenaría en la memoria ... ¿por qué? en una secuencia de comandos de memoria intensiva ... mantener la basura detrás de su no un no y de todos modos ... cada idioma tiene alguna forma de una función var "unset / delete" ... ¿por qué no Python?

Francisco
fuente
77
delno invoca al recolector de basura más rápido que = None, ni dejará basura a la larga. Es posible que desee aprovechar la recolección de basura de Python.
SilverbackNet
-3

Cada objeto en python tiene un identificador, Tipo, recuento de referencia asociado, cuando usamos del recuento de referencia se reduce, cuando el recuento de referencia se convierte en cero, es un candidato potencial para la recolección de basura. Esto diferencia el del cuando se compara con establecer un identificador en Ninguno. En el caso posterior, simplemente significa que el objeto se deja fuera (hasta que estemos fuera del alcance, en cuyo caso se reduce el recuento) y simplemente ahora el punto de identificación a otro objeto (ubicación de memoria).

SaiReddy
fuente
3
Me gustaría ver pruebas de esto. Asignar Ninguno debería disminuir el recuento de referencia.
Jason Baker
3
Esto es una tontería y lo opuesto a la recolección de basura (en el sentido de dejar la basura por ahí).
Pavel Šimerda