Qual é o método __del__, como chamá-lo?

108

Estou lendo um código. Existe uma classe na qual o __del__método é definido. Eu descobri que esse método é usado para destruir uma instância da classe. No entanto, não consigo encontrar um lugar onde esse método seja usado. A principal razão para isso é que eu não sei como este método é usado, provavelmente não é assim: obj1.del(). Então, minha dúvida é como chamar o __del__método?

Verrtex
fonte

Respostas:

168

__del__é um finalizador . É chamado quando um objeto é coletado como lixo, o que acontece em algum ponto depois que todas as referências ao objeto foram excluídas.

Em um caso simples, isso poderia ser logo após você dizer del xou, se xfor uma variável local, após o término da função. Em particular, a menos que haja referências circulares, o CPython (a implementação padrão do Python) fará a coleta de lixo imediatamente.

No entanto, este é um detalhe de implementação do CPython. A única propriedade necessária da coleta de lixo do Python é que ela acontece depois que todas as referências foram excluídas, então isso pode não acontecer necessariamente logo depois e pode nem acontecer .

Ainda mais, as variáveis ​​podem viver por muito tempo por muitos motivos , por exemplo, uma exceção de propagação ou introspecção de módulo pode manter a contagem de referência de variável maior que 0. Além disso, a variável pode ser uma parte do ciclo de referências - CPython com coleta de lixo ativada quebras na maioria , mas não todos, esses ciclos, e mesmo assim, apenas periodicamente.

Já que você não tem garantia de que será executado, nunca se deve colocar o código que você precisa para ser executado __del__()- em vez disso, esse código pertence à finallycláusula do trybloco ou a um gerenciador de contexto em uma withinstrução. No entanto, existem casos de uso válidos para __del__: por exemplo, se um objeto faz Xreferência Ye também mantém uma cópia de Yreferência em um global cache( cache['X -> Y'] = Y), seria educado X.__del__também excluir a entrada do cache.

Se você sabe que o destruidor fornece (em violação da diretriz acima) a limpeza necessária, você pode querer chamá-lo diretamente , uma vez que não há nada de especial sobre ele como um método: x.__del__(). Obviamente, você só deve fazer isso se souber que não se importa em ser chamado duas vezes. Ou, como último recurso, você pode redefinir esse método usando

type(x).__del__ = my_safe_cleanup_method  
ilya n.
fonte
5
Você diz que o recurso do CPython de excluir um objeto imediatamente após sua contagem de referência ser reduzida a zero é um "detalhe de implementação". Não estou convencido. Você pode fornecer um link que respalde essa afirmação? (Quer dizer, a fonte em negrito é bastante convincente por si só, mas os links estão em segundo lugar ... :-)
Stuart Berg
14
Detalhe de implementação de CPython: CPython atualmente usa um esquema de contagem de referência com detecção atrasada (opcional) de lixo ligado ciclicamente, ... Outras implementações agem de forma diferente e CPython pode mudar. ( docs.python.org/2/reference/datamodel.html )
ilya n.
O que há __exit__neste contexto? É executado depois, antes __del__ou junto?
lony
1
O "pode ​​não acontecer" inclui quando o programa termina?
Andy Hayden
1
@AndyHayden: os __del__métodos podem não ser executados mesmo no encerramento do programa e, mesmo quando são executados no encerramento, escrever um __del__método que funcione corretamente mesmo quando o interpretador está ocupado se autodestruindo ao seu redor requer uma codificação mais cuidadosa do que muitos programadores aplicam. (A limpeza do CPython geralmente obtém __del__métodos para serem executados no desligamento do interpretador, mas ainda há casos em que não é suficiente. Threads daemon, globais de nível C e objetos __del__criados em outro __del__podem fazer com que os __del__métodos não sejam executados.)
suporte do user2357112 Monica
80

Escrevi a resposta para outra pergunta, embora esta seja uma pergunta mais precisa para ela.

Como funcionam os construtores e destruidores?

Aqui está uma resposta ligeiramente opinativa.

Não use __del__. Isso não é C ++ ou uma linguagem construída para destruidores. O __del__método realmente deveria desaparecer no Python 3.x, embora eu tenha certeza que alguém encontrará um caso de uso que faça sentido. Se precisar usar __del__, esteja ciente das limitações básicas por http://docs.python.org/reference/datamodel.html :

  • __del__é chamado quando o coletor de lixo está coletando os objetos, não quando você perde a última referência a um objeto e não quando você executa del object.
  • __del__é responsável por chamar qualquer __del__um em uma superclasse, embora não esteja claro se isso está na ordem de resolução de método (MRO) ou apenas chamando cada superclasse.
  • Ter um __del__significa que o coletor de lixo desiste de detectar e limpar quaisquer links cíclicos, como perder a última referência para uma lista vinculada. Você pode obter uma lista dos objetos ignorados em gc.garbage. Às vezes, você pode usar referências fracas para evitar o ciclo completamente. Isso é debatido de vez em quando: consulte http://mail.python.org/pipermail/python-ideas/2009-October/006194.html .
  • A __del__função pode trapacear, salvando uma referência a um objeto e interrompendo a coleta de lixo.
  • As exceções levantadas explicitamente em __del__são ignoradas.
  • __del__complementa __new__muito mais do que __init__. Isso fica confuso. Veja http://www.algorithm.co.il/blogs/programming/python-gotchas-1- del -is-not-the-oposta-of- init / para uma explicação e dicas.
  • __del__não é uma criança "amada" em Python. Você notará que a documentação sys.exit () não especifica se o lixo é coletado antes de sair, e há muitos problemas estranhos. Chamar o __del__on globals causa problemas de ordem estranhos, por exemplo, http://bugs.python.org/issue5099 . Deve ser __del__chamado mesmo se __init__falhar? Consulte http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 para uma longa conversa.

Mas por outro lado:

E meu motivo pessoal para não gostar da __del__função.

  • Sempre que alguém __del__fala, isso se transforma em trinta mensagens de confusão.
  • Ele quebra esses itens no Zen do Python:
    • Simples é melhor do que complicado.
    • Casos especiais não são especiais o suficiente para quebrar as regras.
    • Os erros nunca devem passar silenciosamente.
    • Diante da ambigüidade, recuse a tentação de adivinhar.
    • Deve haver uma - e de preferência apenas uma - maneira óbvia de fazer isso.
    • Se a implementação for difícil de explicar, é uma má ideia.

Portanto, encontre um motivo para não usar __del__.

Charles Merriam
fonte
6
Mesmo que a pergunta não seja exatamente: por que não devemos usar __del__, mas como ligar __del__, sua resposta é interessante.
novembro
Obrigado. Às vezes, a melhor ideia é evitar ideias terríveis.
Charles Merriam
Em outras notícias, esqueci de mencionar que o PyPy (um interpretador mais rápido para aplicativos de execução mais longa) irá quebrar no del .
Charles Merriam
Obrigado @Gloin por atualizar o link quebrado!
Charles Merriam de
@CharlesMerriam Obrigado você para a resposta!
Tom Burrows,
13

O __del__método, ele será chamado quando o objeto for coletado como lixo. Observe que não é necessariamente garantido que ele seja chamado. O código a seguir por si só não fará isso necessariamente:

del obj

A razão é que delapenas diminui a contagem de referência em um. Se algo mais tiver uma referência ao objeto, __del__não será chamado.

No __del__entanto, existem algumas advertências para usar . Geralmente, eles geralmente simplesmente não são muito úteis. Parece-me mais que você deseja usar um método de fechamento ou talvez uma instrução with .

Consulte a documentação__del__ do python sobre métodos .

Outra coisa a ser observada: os __del__métodos podem inibir a coleta de lixo se usados ​​em excesso. Em particular, uma referência circular que tem mais de um objeto com um __del__método não será coletada como lixo. Isso ocorre porque o coletor de lixo não sabe qual chamar primeiro. Veja a documentação do módulo gc para mais informações.

Jason Baker
fonte
8

O __del__método (observe a ortografia!) É chamado quando o objeto é finalmente destruído. Tecnicamente falando (em cPython) é quando não há mais referências ao seu objeto, ou seja, quando ele sai do escopo.

Se você deseja deletar seu objeto e assim chamar o __del__método, use

del obj1

que excluirá o objeto (desde que não haja nenhuma outra referência a ele).

Eu sugiro que você escreva uma pequena classe como esta

class T:
    def __del__(self):
        print "deleted"

E investigue no interpretador python, por exemplo

>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
...     a = T()
...     print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>   

Observe que jython e ironpython têm regras diferentes sobre exatamente quando o objeto é excluído e __del__chamado. Não é considerado uma boa prática usar, __del__porém, por causa disso e do fato de que o objeto e seu ambiente podem estar em um estado desconhecido quando ele é chamado. Também não há garantia absoluta de __del__que será chamado - o intérprete pode sair de várias maneiras sem excluir todos os objetos.

Nick Craig-Wood
fonte
1
em comparação com stackoverflow.com/a/2452895/611007 e stackoverflow.com/a/1481512/611007 , use del obj1parece uma má ideia confiar.
n611x007 de
0

Conforme mencionado anteriormente, a __del__funcionalidade não é confiável. Nos casos em que pode parecer útil, considere usar os métodos __enter__e __exit__. Isso dará um comportamento semelhante aowith open() as f: pass sintaxe usada para acessar arquivos. __enter__é chamado automaticamente ao entrar no escopo de with, enquanto __exit__é chamado automaticamente ao sair dele. Veja esta pergunta para mais detalhes.

somoria
fonte