class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
acima falha com uma exceção AttributeError. Entendo que o Python não garante a existência de "variáveis globais" (dados de membros neste contexto?) Quando __del__()
é invocado. Se for esse o caso e esse for o motivo da exceção, como posso garantir que o objeto seja destruído corretamente?
python
destructor
wilhelmtell
fonte
fonte
__del__
que não deve ser usado como contrapartida__init__
. (Ou seja, não é um "destruidor" no sentido em que__init__
é um construtor.Respostas:
Eu recomendo usar a
with
declaração do Python para gerenciar recursos que precisam ser limpos. O problema com o uso de umaclose()
declaração explícita é que você precisa se preocupar com as pessoas que se esquecem de chamá-la ou se esquecem de colocá-la em umfinally
bloco para evitar que um recurso vaze quando ocorrer uma exceção.Para usar a
with
instrução, crie uma classe com os seguintes métodos:No seu exemplo acima, você usaria
Então, quando alguém quisesse usar sua classe, faria o seguinte:
A variável package_obj será uma instância do tipo Package (é o valor retornado pelo
__enter__
método). Seu__exit__
método será chamado automaticamente, independentemente de ocorrer ou não uma exceção.Você pode até levar essa abordagem um passo adiante. No exemplo acima, alguém ainda pode instanciar o Package usando seu construtor sem usar a
with
cláusula Você não quer que isso aconteça. Você pode corrigir isso criando uma classe PackageResource que define o__enter__
e__exit__
métodos. Em seguida, a classe Package seria definida estritamente dentro do__enter__
método e retornada. Dessa forma, o chamador nunca poderia instanciar a classe Package sem usar umawith
instrução:Você usaria isso da seguinte maneira:
fonte
with Resource(param1, param2) as r: # ...
A maneira padrão é usar
atexit.register
:Mas você deve ter em mente que isso persistirá em todas as instâncias criadas
Package
até que o Python seja encerrado.Demonstração usando o código acima salvo como package.py :
fonte
with
? Eles explicitamente ligaram__enter__
?) A desvantagem é obviamente se você precisar que a limpeza ocorra antes do python sai, não vai funcionar. No meu caso, não me importo se é quando o objeto sai do escopo ou se não é até a saída do python. :)atexit.register(self.__exit__)
?__exit__
e usar um gerenciador de contexto? Além disso,__exit__
leva argumentos adicionais (ou seja__exit__(self, type, value, traceback)
), então você precisaria contá-los. De qualquer maneira, parece que você deve postar uma pergunta separada no SO, porque seu caso de uso parece incomum?Como um apêndice à resposta de Clint , você pode simplificar
PackageResource
usandocontextlib.contextmanager
:Como alternativa, embora provavelmente não seja Pythonic, você pode substituir
Package.__new__
:e simplesmente use
with Package(...) as package
.Para simplificar , nomeie sua função de limpeza
close
e usecontextlib.closing
; nesse caso, você pode usar aPackage
classe não modificadawith contextlib.closing(Package(...))
ou substituí__new__
-la pela mais simplesE esse construtor é herdado, então você pode simplesmente herdar, por exemplo
fonte
Package.__new__()
método, no entanto. Ou talvez possamos. Provavelmente, poderíamos definir um decorador de classe ou uma metaclasse que genere esse padrão para nós. Alimento para o pensamento pitônico.Package
também deve fazer isso (embora eu ainda não testei), portanto, nenhuma metaclasse deve ser necessária. Apesar de eu ter encontrado algumas maneiras muito curiosos para utilizar metaclasses no passado ...Package
(ou melhor, uma classe nomeadaClosing
) como pai da classe em vez deobject
. Mas não me pergunte como messes herança múltipla com isso ...Eu não acho que é possível, por exemplo, remover membros antes de
__del__
ser chamado. Meu palpite seria que o motivo do seu AttributeError específico está em outro lugar (talvez você remova o self.file por engano em outro lugar).No entanto, como os outros apontaram, você deve evitar o uso
__del__
. A principal razão para isso é que as instâncias com__del__
não serão coletadas como lixo (elas serão liberadas somente quando a contagem de refechas chegar a 0). Portanto, se suas instâncias estiverem envolvidas em referências circulares, elas permanecerão na memória enquanto o aplicativo for executado. (Posso estar enganado sobre tudo isso, porém, eu precisaria ler os documentos do gc novamente, mas tenho certeza de que funciona assim).fonte
__del__
podem ser coletados com lixo se a contagem de referência de outros objetos com__del__
for zero e eles estiverem inacessíveis. Isso significa que se você tiver um ciclo de referência entre objetos com__del__
, nenhum deles será coletado. Qualquer outro caso, no entanto, deve ser resolvido conforme o esperado.Uma alternativa melhor é usar o fracoref.finalize . Veja os exemplos em Objetos do finalizador e Comparando finalizadores com os métodos __del __ () .
fonte
stop()
método para fechar as portas ejoin()
os processos. No entanto, se o programa sair inesperadamente,stop()
não será chamado - resolvi isso com um finalizador. Mas, em qualquer caso, eu chamo_finalizer.detach()
o método stop para evitar chamá-lo duas vezes (manualmente e depois novamente pelo finalizador).Eu acho que o problema poderia estar
__init__
se houver mais código do que o mostrado?__del__
será chamado mesmo quando__init__
não for executado corretamente ou gerou uma exceção.Fonte
fonte
__del__
é declarar explicitamente todos os membros no nível da classe, garantindo que eles sempre existam, mesmo que__init__
falhem. No exemplo dado,files = ()
funcionaria, embora na maioria das vezes você acabasse de atribuirNone
; nos dois casos, você ainda precisará atribuir o valor real em__init__
.Aqui está um esqueleto de trabalho mínimo:
Importante: retornar a si mesmo
Se você é como eu, e ignora a
return self
parte (da resposta correta de Clint Miller ), estará olhando para essa bobagem:Espero que ajude a próxima pessoa.
fonte
Apenas envolva seu destruidor com uma instrução try / except e ela não emitirá uma exceção se seus globais já estiverem descartados.
Editar
Tente o seguinte:
Ele irá preencher a lista de arquivos na função del que é garantida a existência no momento da chamada. O proxy weakref é impedir que o Python ou você mesmo exclua a variável self.files de alguma forma (se for excluída, não afetará a lista de arquivos original). Se não for o caso de isso estar sendo excluído, mesmo que haja mais referências à variável, você poderá remover o encapsulamento do proxy.
fonte
Parece que a maneira idiomática de fazer isso é fornecer um
close()
método (ou similar) e chamá-lo explicitamente.fonte