Quando del é útil em python?

374

Eu realmente não consigo pensar em nenhuma razão para o python precisar da delpalavra - chave (e a maioria das linguagens parece não ter uma palavra-chave semelhante). Por exemplo, em vez de excluir uma variável, pode-se apenas atribuir Nonea ela. E ao excluir de um dicionário, um delmétodo pode ser adicionado.

Existe alguma razão para se manter delem python ou é um vestígio dos dias de coleta de lixo pré-lixo do Python?

Jason Baker
fonte
46
Nota histórica : Python teve coleta de lixo desde o início. Antes da versão 2.0, o coletor de lixo do Python não conseguia detectar ciclos de referências, mas isso não tinha nada a ver del.
Steven Rumbalski 27/05
28
@Steven Rumbalksi, tem algo a ver com del. Del foi usado para quebrar os ciclos de referência.
Winston Ewert
6
mas delnão é um vestígio da coleta pré-lixo, porque você sempre pode ter used = None. Sempre fazia sentido ter uma sintaxe específica para isso. Como agora temos GC cilíndrico, os casos em que você deseja usar são pequenos.
Winston Ewert 27/05
3
> e a maioria dos idiomas parece não ter uma palavra-chave semelhante A maioria dos idiomas coletados de lixo não possui (ou apenas o utiliza para sugerir ao GC que ele pode coletar uma variável). A maioria linguagem mais antiga fez: - Bons velhos idiomas básicos como QuickBasic tinha ERASE(variáveis matar específicos) e CLEAR(matar todas as variáveis)
DrYak

Respostas:

499

Primeiramente, você pode deletar outras coisas além das variáveis ​​locais

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

Ambos devem ser claramente úteis. Em segundo lugar, o uso delem uma variável local torna a intenção mais clara. Comparar:

del foo

para

foo = None

Sei que, no caso, del fooa intenção é remover a variável do escopo. Não está claro o que foo = Noneestá fazendo isso. Se alguém acabou de atribuir foo = Noneeu poderia pensar que era um código morto. Mas eu instantaneamente sei o que alguém que codifica del fooestava tentando fazer.

Winston Ewert
fonte
51
+1, sim. Quando você atribui algo, ele transmite a intenção de usá-lo mais tarde. Eu realmente só vi delusado em cálculos com muita memória, mas quando o vi, percebi imediatamente por que era necessário.
detly 27/05
17
O caso de uso da exclusão de uma lista ou dicionário pode ser facilmente substituído por um método (como observei na pergunta). Não sei se concordo com o uso de delpara sinalizar intenção (como um comentário poderia fazer o mesmo sem aumentar o idioma), mas suponho que essa seja a melhor resposta.
Jason Baker
6
@JasonBaker, concedido pelos métodos. Excluir fatias e coisas assim seria mais complicado usando um método. Sim, você pode usar um comentário. Mas acho que usar uma declaração é melhor do que um comentário como parte da linguagem.
Winston Ewert
2
@ JasonBaker: Não se trata apenas da intenção, essas duas sintaxes fazem duas coisas muito diferentes.
Pavel Šimerda
2
Também é tratado excluindo-o do respectivo dicionário. Você também pode contestar a len()função da mesma maneira. Se for esse o caso, a pergunta pode ter sido encerrada como opinião ou mesmo com base no gosto. O Python simplesmente tende a fornecer primitivas para operações básicas, em vez de depender de métodos.
Pavel Šimerda
161

Existe esta parte do que delfaz (da Referência da linguagem Python ):

A exclusão de um nome remove a ligação desse nome do espaço para nome local ou global

A atribuição Nonea um nome não remove a ligação do nome do espaço para nome.

(Suponho que possa haver algum debate sobre se a remoção de uma ligação de nome é realmente útil , mas essa é outra questão.)

Greg Hewgill
fonte
22
-1 O pôster claramente já entende isso, ele está perguntando por que você deseja remover uma ligação de nome.
Winston Ewert 27/05
71
@Winston Ewert: Não sei se o pôster entendeu que delremove uma ligação de nome, como ele sugeriu atribuir Nonecomo alternativa.
Steven Rumbalski 27/05
8
@Steven, o pôster contrasta claramente a exclusão da variável (remoção do nome) e a atribuição de Nenhuma. Ele não vê por que você deve excluir a variável quando pode apenas atribuir None. Eles têm o mesmo efeito, na medida em que liberam a referência ao que anteriormente estava vinculado a esse nome.
Winston Ewert
19
@Winston Ewert: Não está claro para mim. Talvez esteja claro para você que afirma que "têm o mesmo efeito, pois liberam a referência ao que estava anteriormente vinculado a esse nome". Mas essa não é (claramente?) A história toda, na qual uma tentativa de usar um nome após excluí-lo gera a NameError. Greg Hewgill faz essa mesma distinção. E é essa distinção que deixa clara a minha afirmação do que o pôster "claramente" entendeu.
Steven Rumbalski 27/05
9
@Winston Ewert Não concordo. Mas já disse o suficiente. Nós dois fizemos nossos casos.
Steven Rumbalski
44

Um lugar que eu achei delútil é limpar variáveis ​​estranhas nos loops de:

for x in some_list:
  do(x)
del x

Agora você pode ter certeza de que x será indefinido se você o usar fora do loop for.

Jason Baker
fonte
11
Sua dellinha aqui deve estar recuada (isto é, parte do loop)?
Sam
11
@ Sam, não, eles não se destinam.
user5319825
7
@ Sam Não, a idéia aqui é que, após a última iteração do loop (após executar o () no elemento final de some_list), x continuaria sendo uma referência ao valor final de some_list. del x garante que isso não permaneça atribuído como tal.
Matthew
12
Isso causará NameError: name 'x' is not definedse a lista estiver vazia.
WofWca 26/06/19
18

Excluir uma variável é diferente de defini-la como None

A exclusão de nomes de variáveis ​​com delé provavelmente algo usado raramente, mas é algo que não poderia ser alcançado trivialmente sem uma palavra-chave. Se você pode criar um nome de variável escrevendo a=1, é bom que você possa desfazê-lo teoricamente excluindo a.

Isso pode facilitar a depuração em alguns casos, pois a tentativa de acessar uma variável excluída gera um NameError.

Você pode excluir atributos da instância de classe

Python permite escrever 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")

Se você optar por adicionar atributos dinamicamente a uma instância de classe, certamente desejará desfazê-lo escrevendo

del a.a
Bernhard
fonte
16

Há um exemplo específico de quando você deve usar del(pode haver outros, mas eu sei disso imediatamente) quando você está usando sys.exc_info()para inspecionar uma exceção. Essa função retorna uma tupla, o tipo de exceção que foi gerada, a mensagem e um retorno.

Os dois primeiros valores geralmente são suficientes para diagnosticar um erro e agir sobre ele, mas o terceiro contém toda a pilha de chamadas entre onde a exceção foi gerada e onde a exceção foi capturada. Em particular, se você fizer algo como

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

o retorno, tbacaba nos locais da pilha de chamadas, criando uma referência circular que não pode ser coletada como lixo. Assim, é importante fazer:

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

para quebrar a referência circular. Em muitos casos em que você gostaria de chamar sys.exc_info(), como na magia da metaclasse, o traceback é útil, portanto, você deve limpá-lo antes de poder sair do manipulador de exceções. Se você não precisar do rastreamento, exclua-o imediatamente ou faça:

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

Para evitar tudo isso junto.

SingleNegationElimination
fonte
18
Não é mais verdade que o coletor de lixo não irá colecioná-lo. No entanto, o ciclo atrasará a coleta.
Winston Ewert 27/05
Isso não é muito relevante e não responde à pergunta do op.
WhyNotHugo
15

Apenas outro pensamento.

Ao depurar aplicativos http em estruturas como o Django, a pilha de chamadas cheia de variáveis ​​inúteis e bagunçadas usadas anteriormente, especialmente quando é uma lista muito longa, pode ser muito dolorosa para os desenvolvedores. portanto, nesse ponto, o controle de namespace pode ser útil.

tdihp
fonte
12

Usar explicitamente "del" também é uma prática melhor do que atribuir uma variável a None. Se você tentar deletar uma variável que não existe, você receberá um erro de tempo de execução, mas se tentar definir uma variável que não exista como None, o Python silenciosamente definirá uma nova variável como None, deixando a variável que você queria excluído onde estava. Então, del irá ajudá-lo a detectar seus erros mais cedo

Matt
fonte
11

Para adicionar alguns pontos às respostas acima: del x

A definição de xindica r -> o(uma referência rapontando para um objeto o), mas del xmuda em rvez de o. É uma operação na referência (ponteiro) ao objeto e não ao objeto associado x. Distinguir entre re oé a chave aqui.

  • Ele remove de locals().
  • Remove de globals()se xpertence lá.
  • Remove-o do quadro de pilha (remove a referência fisicamente dele, mas o próprio objeto reside no pool de objetos e não no quadro de pilha).
  • Remove do escopo atual. É muito útil limitar o período de definição de uma variável local, que de outra forma pode causar problemas.
  • É mais sobre declaração do nome do que definição de conteúdo.
  • Afeta onde xpertence e não para onde xaponta. A única mudança física na memória é essa. Por exemplo, se xestiver em um dicionário ou lista, ele (como referência) é removido de lá (e não necessariamente do pool de objetos). Neste exemplo, o dicionário ao qual ele pertence é o quadro da pilha ( locals()), sobreposto globals().
Sohail Si
fonte
6

Forçar o fechamento de um arquivo após o uso de numpy.load:

Um uso de nicho, talvez, mas achei útil ao usar numpy.loadpara ler um arquivo. De vez em quando eu atualizava o arquivo e precisava copiar um arquivo com o mesmo nome para o diretório.

Eu costumava delliberar o arquivo e me permitir copiar o novo arquivo.

Note que eu quero evitar o withgerenciador de contexto, pois estava brincando com gráficos na linha de comando e não queria pressionar muito a tecla Tab!

Veja esta pergunta.

atomh33ls
fonte
Eu tinha algo semelhante com imagens carregadas com a Python Image Library (PIL). Abro uma imagem e, se ela tivesse certas dimensões, queria excluir o arquivo; no entanto, o arquivo ainda estava em uso pelo Python. Portanto, eu disse 'del img' e, em seguida, poderia remover o arquivo.
physicalattraction
2
Lembre-se de que este é um detalhe de implementação, pois a especificação da linguagem não garante quando o __del__()método nos objetos de lixo é chamado ou mesmo se é chamado. Portanto, as APIs que não oferecem outra maneira de liberar recursos, além de esperar que o __del__()método seja chamado algum tempo (logo após o objeto ser lixo), são quebradas em algum grau.
precisa
6

delé frequentemente visto em __init__.pyarquivos. Qualquer variável global definida em um __init__.pyarquivo é automaticamente "exportada" (será incluída em a from module import *). Uma maneira de evitar isso é definir __all__, mas isso pode ficar confuso e nem todo mundo usa.

Por exemplo, se você tivesse código __init__.pycomo

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

Então seu módulo exportaria o sysnome. Você deveria escrever

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

del sys
asmeurer
fonte
4

Como um exemplo do que delpode ser usado, acho útil em situações 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 duas funções podem estar em diferentes pacotes / módulos eo programador não precisa saber o argumento valor padrão cem frealmente tem. Portanto, usando kwargs em combinação com del, você pode dizer "Quero o valor padrão em c", definindo-o como None (ou, neste caso, também o deixe).

Você poderia fazer a mesma coisa com algo como:

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

    return f(**kwargs)

No entanto, acho o exemplo anterior mais SECO e elegante.

Jocke
fonte
3

Quando del é útil em python?

Você pode usá-lo para remover um único elemento de uma matriz em vez da sintaxe da fatia x[i:i+1]=[]. Isso pode ser útil se, por exemplo, você estiver dentro os.walke desejar excluir um elemento no diretório Porém, eu não consideraria uma palavra-chave útil para isso, pois é possível criar um [].remove(index)método (o .removemétodo é realmente pesquisar e remover a primeira instância do valor).

ninjagecko
fonte
7
[].pop(index)e [].remove(item). Não use a variável nomeada "index"ao falar sobre valor, isso parecerá confuso.
Ski
O @Ski pop usa índice . Esta é uma resposta válida, enquanto metade dessas respostas fornece apenas um exemplo usando del, em que None também funcionaria. Um objeto de lista definido como Nenhum ainda está na lista, enquanto del remove os itens.
user5389726598465
3

Eu achei delútil para o gerenciamento de memória pseudo-manual ao lidar com grandes dados com o Numpy. Por exemplo:

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

Essa pode ser a diferença entre interromper um processo de trituração, à medida que o sistema muda como louco quando o GC Python não consegue acompanhar o funcionamento perfeitamente suave abaixo de um limite de memória fraco que deixa bastante espaço para usar a máquina para navegar. e codifique enquanto estiver trabalhando.

Nervo
fonte
2

Eu acho que uma das razões pelas quais del tem sua própria sintaxe é que substituí-lo por uma função pode ser difícil em certos casos, uma vez que opera na ligação ou variável e não no valor que ele faz referência. Assim, se uma versão funcional de del fosse criada, um contexto precisaria ser passado. Del foo precisaria se tornar global (). Remove ('foo') ou locals (). Remove ('foo') que fica confuso e menos legível. Ainda assim, digo que se livrar de del seria bom, devido ao seu uso aparentemente raro. Mas remover recursos / falhas de idioma pode ser doloroso. Talvez o python 4 o remova :)

waffle difuso
fonte
2

Eu gostaria de elaborar a resposta aceita para destacar a nuance entre definir uma variável como Noneversus removê-la com del:

Dada a variável foo = 'bar'e a seguinte definição de função:

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

Uma vez declarado inicialmente, test_var(foo)produz variable tested trueconforme o esperado.

Agora tente:

foo = None
test_var(foo)

qual produz variable tested false.

Compare esse comportamento com:

del foo
test_var(foo)

que agora aumenta NameError: name 'foo' is not defined.

jacob
fonte
0

Ainda outro uso de nicho: no pyroot com ROOT5 ou ROOT6, "del" pode ser útil para remover um objeto python que se refere a um objeto C ++ não existente. Isso permite que a pesquisa dinâmica do pyroot encontre um objeto C ++ com nome idêntico e vincule-o ao nome do python. Então você pode ter um cenário 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

Felizmente, esse nicho será fechado com o gerenciamento de objetos mais saudável do ROOT7.

Amnon Harel
fonte
0

O comando "del" é muito útil para controlar dados em uma matriz, por exemplo:

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

Resultado:

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

Victor Marrerp
fonte
-2

Uma vez eu tive que usar:

del serial
serial = None

porque usando apenas:

serial = None

não liberou a porta serial com rapidez suficiente para abri-la imediatamente novamente. Com a lição, aprendi que isso delrealmente significava: "GC now NOW! E espere até que esteja pronto", e isso é realmente útil em muitas situações. Claro, você pode ter um system.gc.del_this_and_wait_balbalbalba(obj).

alfredolavina
fonte
5
Hmm ... Isso realmente não deveria ter feito diferença. Embora, talvez o seu problema tenha sido corrigido pelo atraso extra introduzido?
Winston Ewert 28/11
3
Eu não acho que você pode apoiar o GC agora por qualquer documentação. Eu acho que confiar no GC e chamar __del__()sempre está errado no Python (embora eu não conheça os motivos desse design) e é melhor usar a API do gerenciador de contexto (a withdeclaração).
Pavel Šimerda
11
Isso é algum tipo de programação vodu. Consulte a descrição do método e a nota na documentação do objeto .__ del __ () para obter detalhes, e lembre-se de que isso descreve apenas a implementação atual do CPythons com contagem de referência. Outras implementações do Python (PyPy, Jython, IronPython, Brython,…) ou futuras implementações do CPython podem usar um esquema de coleta de lixo diferente. O Jython usa o GC das JVMs, que não exclui objetos imediatamente. O serialmódulo também funciona com o Jython para que seu hack não funcione lá!
BlackJack 13/08
BTW gc.collect seria o caminho para reciclar explicitamente. Suportado na maioria das implementações python.
Tdihp 28/10/2015
-2

del é o equivalente a "unset" em muitas linguagens e como um ponto de referência cruzado que passa de outra linguagem para python. as pessoas tendem a procurar comandos que fazem a mesma coisa que costumavam fazer na primeira língua ... também definindo um var para "" ou nenhum realmente não remove o var do escopo .. apenas esvazia seu valor, o nome do var ainda estaria armazenado na memória ... por quê?!? em um script intensivo de memória ... mantendo o lixo por trás do seu simplesmente não, não e de qualquer maneira ... todas as línguas por aí têm alguma forma de uma função var "unset / delete" .. por que não python?

Francisco
fonte
7
delnão invoca o coletor de lixo mais rapidamente do que = Nonenem deixa o lixo para trás a longo prazo. Você pode querer desenterrar a coleta de lixo do Python.
precisa
-3

Todo objeto em python tem um identificador, Type, contagem de referência associada a ele, quando usamos del a contagem de referência é reduzida, quando a contagem de referência se torna zero, é um candidato em potencial à coleta de lixo. Isso diferencia o del quando comparado à configuração de um identificador como Nenhum. Em um caso posterior, significa simplesmente que o objeto é deixado de fora do estado selvagem (até ficarmos fora do escopo, nesse caso, a contagem é reduzida) e agora o identificador simplesmente aponta para outro objeto (localização da memória).

SaiReddy
fonte
3
Eu gostaria de ver uma prova disso. Atribuindo None deve diminuir a contagem de referência.
Jason Baker
3
Isso é um absurdo e o oposto da coleta de lixo (no sentido de deixar o lixo por aí).
Pavel Šimerda