"weakref" --- Referências fracas
********************************

**Código-fonte:** Lib/weakref.py

======================================================================

O módulo "weakref" permite ao programador Python criar *referências
fracas* para objetos.

A seguir, o termo *referente* significa o objeto ao qual é referido
por uma referência fraca.

Uma referência fraca a um objeto não é suficiente para mantê-lo vivo:
quando as únicas referências restantes a um referente são referências
fracas, a *coleta de lixo* está livre para destruir o referente e
reutilizar sua memória para outra coisa. Entretanto, até que o objeto
seja realmente destruído, a referência fraca poderá retornar o objeto
mesmo que não haja referências fortes a ele.

Um uso principal para referências fracas é implementar caches ou
mapeamentos contendo objetos grandes, onde é desejado que um objeto
grande não seja mantido ativo apenas porque aparece em um cache ou
mapeamento.

Por exemplo, se você tiver vários objetos de imagem binária grandes,
poderá associar um nome a cada um. Se você usasse um dicionário Python
para mapear nomes para imagens, ou imagens para nomes, os objetos de
imagem permaneceriam vivos apenas porque apareceriam como valores ou
chaves nos dicionários. As classes "WeakKeyDictionary" e
"WeakValueDictionary" fornecidas pelo módulo "weakref" são uma
alternativa, usando referências fracas para construir mapeamentos que
não mantêm objetos vivos apenas porque aparecem nos objetos de
mapeamento. Se, por exemplo, um objeto de imagem for um valor em um
"WeakValueDictionary", então quando as últimas referências restantes a
esse objeto de imagem forem as referências fracas mantidas por
mapeamentos fracos, a coleta de lixo poderá recuperar o objeto e suas
entradas correspondentes em mapeamentos fracos são simplesmente
excluídos.

"WeakKeyDictionary" e "WeakValueDictionary" usam referências fracas em
sua implementação, configurando funções de retorno de chamada nas
referências fracas que notificam os dicionários fracos quando uma
chave ou valor foi recuperado pela coleta de lixo. "WeakSet"
implementa a interface "set", mas mantém referências fracas aos seus
elementos, assim como "WeakKeyDictionary" faz.

"finalize" fornece uma maneira direta de registrar uma função de
limpeza a ser chamada quando um objeto é coletado como lixo. Isso é
mais simples de usar do que configurar uma função de retorno de
chamada em uma referência fraca não tratada, pois o módulo garante
automaticamente que o finalizador permaneça ativo até que o objeto
seja coletado.

A maioria dos programas deve descobrir que usar um desses tipos de
contêineres fracos ou "finalize" é tudo que eles precisam --
geralmente não é necessário criar suas próprias referências fracas
diretamente. O maquinário de baixo nível é exposto pelo módulo
"weakref" para benefício de usos avançados.

Nem todos os objetos podem ser referenciados de maneira fraca. Objetos
que oferecem suporte a referências fracas incluem instâncias de
classe, funções escritas em Python (mas não em C), métodos de
instância, conjuntos, frozensets, alguns *objetos arquivos*,
*geradores*, objetos tipo, sockets, arrays, deques, objetos padrão de
expressão regular e objetos código.

Alterado na versão 3.2: Adicionado suporte para thread.lock,
threading.Lock e objetos código.

Vários tipos embutidos como "list" e "dict" não oferecem suporta
diretamente a referências fracas, mas podem adicionar suporte através
de subclasses:

   class Dict(dict):
       pass

   obj = Dict(red=1, green=2, blue=3)   # este objeto é potencialmente fracamente referenciável

Outros tipos embutidos como "tuple" e "int" não oferecem suporte a
referências fracas mesmo em subclasses.

Os tipos de extensão podem ser facilmente criados para oferecer
suporte a referências fracas; veja Weak Reference Support.

Quando "__slots__" são definidos para um determinado tipo, o suporte a
referência fraca é desativado a menos que uma string "'__weakref__'"
também esteja presente na sequência de strings na declaração de
"__slots__". Veja a documentação de __slots__ para detalhes.

class weakref.ref(object[, callback])

   Retorna uma referência fraca para *object*. O objeto original pode
   ser recuperado chamando o objeto referência se o referente ainda
   estiver ativo; se o referente não estiver mais ativo, chamar o
   objeto referência fará com que "None" seja retornado. Se *callback*
   for fornecido e não for "None", e o objeto referência fraca
   retornado ainda estiver ativo, o função de retorno será chamada
   quando o objeto estiver prestes a ser finalizado; o objeto
   referência fraca será passado como único parâmetro para a função de
   retorno; o referente não estará mais disponível.

   É permitido que muitas referências fracas sejam construídas para o
   mesmo objeto. As funções de retorno registradas para cada
   referência fraca serão chamadas da função de retorno registrada
   mais recentemente para a função de retorno registrada mais antiga.

   As exceções levantadas pela função de retorno serão anotadas na
   saída de erro padrão, mas não poderão ser propagadas; elas são
   tratadas exatamente da mesma maneira que as exceções levantadas
   pelo método "__del__()" de um objeto.

   Referências fracas são *hasheáveis* se o *object* for hasheável.
   Elas manterão seu valor de hash mesmo depois que *object* for
   excluído. Se "hash()" for chamada pela primeira vez somente após o
   *object* ter sido excluído, a chamada vai levantar "TypeError".

   Referências fracas oferecem suporte a testes de igualdade, mas não
   de ordenação. Se os referentes ainda estiverem vivos, duas
   referências terão a mesma relação de igualdade que seus referentes
   (independentemente do *callback*). Se um dos referentes tiver sido
   excluído, as referências serão iguais somente se os objetos
   referência forem o mesmo objeto.

   Este é um tipo do qual pode ser feita subclasse em vez de uma
   função de fábrica.

   __callback__

      Este atributo somente leitura retorna a função de retorno
      atualmente associada à referência fraca. Se não houver função de
      retorno ou se o referente da referência fraca não estiver mais
      ativo, então este atributo terá o valor "None".

   Alterado na versão 3.4: Adicionado o atributo "__callback__".

weakref.proxy(object[, callback])

   Retorna um intermediário (proxy, em inglês) para *object* que usa
   uma referência fraca. Isto provê suporte ao uso do intermediário na
   maioria dos contextos, em vez de exigir a desreferenciação
   explícita usada com objetos de referência fraca. O objeto retornado
   terá um tipo "ProxyType" ou "CallableProxyType", dependendo se
   *object* é chamável. Objetos intermediários não são *hasheáveis*
   independentemente do referente; isso evita uma série de problemas
   relacionados à sua natureza fundamentalmente mutável e impede seu
   uso como chaves de dicionário. *callback* é igual ao parâmetro de
   mesmo nome da função "ref()".

   Acessar um atributo do objeto intermediário depois que o referente
   é coletado como lixo levanta "ReferenceError".

   Alterado na versão 3.8: Estendeu o suporte ao operador em objetos
   intermediários para incluir os operadores "@" e "@=" para
   multiplicação de matrizes.

weakref.getweakrefcount(object)

   Retorna o número de referências fracas e intermediários que fazem
   referência a *object*.

weakref.getweakrefs(object)

   Retorna uma lista de todos os objetos intermediários e de
   referência fraca que fazem referência a *object*.

class weakref.WeakKeyDictionary([dict])

   Classe de mapeamento que faz referência fraca a chaves. Entradas no
   dicionário serão descartadas quando não houver mais uma referência
   forte à chave. Isso pode ser usado para associar dados adicionais a
   um objeto de propriedade de outras partes de uma aplicação sem
   adicionar atributos a esses objetos. Isso pode ser especialmente
   útil com objetos que substituem acessos de atributos.

   Note que quando uma chave com valor igual a uma chave existente
   (mas não identidade igual) é inserida no dicionário, ela substitui
   o valor, mas não substitui a chave existente. Devido a isso, quando
   a referência à chave original é excluída, ela também exclui a
   entrada no dicionário:

      >>> class T(str): pass
      ...
      >>> k1, k2 = T(), T()
      >>> d = weakref.WeakKeyDictionary()
      >>> d[k1] = 1   # d = {k1: 1}
      >>> d[k2] = 2   # d = {k1: 2}
      >>> del k1      # d = {}

   Uma solução alternativa seria remover a chave antes da
   reatribuição:

      >>> class T(str): pass
      ...
      >>> k1, k2 = T(), T()
      >>> d = weakref.WeakKeyDictionary()
      >>> d[k1] = 1   # d = {k1: 1}
      >>> del d[k1]
      >>> d[k2] = 2   # d = {k2: 2}
      >>> del k1      # d = {k2: 2}

   Alterado na versão 3.9: Adicionado suporte para operadores "|" e
   "|=", conforme especificado na **PEP 584**.

Objetos "WeakKeyDictionary" têm um método adicional que expõe as
referências internas diretamente. Não há garantia de que as
referências estejam "ativas" no momento em que são usadas, então o
resultado da chamada das referências precisa ser verificado antes de
ser usado. Isso pode ser usado para evitar a criação de referências
que farão com que o coletor de lixo mantenha as chaves por mais tempo
do que o necessário.

WeakKeyDictionary.keyrefs()

   Retorna um iterável das referências fracas às chaves.

class weakref.WeakValueDictionary([dict])

   Classe de mapeamento que faz referência fraca a valores. Entradas
   no dicionário serão descartadas quando nenhuma referência forte ao
   valor existir mais.

   Alterado na versão 3.9: Adicionado suporte para operadores "|" e
   "|=", conforme especificado na **PEP 584**.

Os objetos "WeakValueDictionary" têm um método adicional que apresenta
os mesmos problemas que o método "WeakKeyDictionary.keyrefs()".

WeakValueDictionary.valuerefs()

   Retorna um iterável das referências fracas aos valores.

class weakref.WeakSet([elements])

   Define a classe que mantém referências fracas para seus elementos.
   Um elemento será descartado quando nenhuma referência forte a ele
   existir mais.

class weakref.WeakMethod(method[, callback])

   Uma subclasse personalizada de "ref" que simula uma referência
   fraca a um método vinculado (ou seja, um método definido em uma
   classe e pesquisado em uma instância). Como um método vinculado é
   efêmero, uma referência fraca padrão não pode mantê-lo.
   "WeakMethod" tem um código especial para recriar o método vinculado
   até que o objeto ou a função original morra:

      >>> class C:
      ...     def method(self):
      ...         print("method called!")
      ...
      >>> c = C()
      >>> r = weakref.ref(c.method)
      >>> r()
      >>> r = weakref.WeakMethod(c.method)
      >>> r()
      <bound method C.method of <__main__.C object at 0x7fc859830220>>
      >>> r()()
      method called!
      >>> del c
      >>> gc.collect()
      0
      >>> r()
      >>>

   *callback* é o mesmo que o parâmetro de mesmo nome da função
   "ref()".

   Adicionado na versão 3.4.

class weakref.finalize(obj, func, /, *args, **kwargs)

   Retorna um objeto finalizador chamável que será chamado quando
   *obj* for coletado como lixo. Diferentemente de uma referência
   fraca comum, um finalizador sempre sobreviverá até que o objeto
   referência seja coletado, simplificando muito o gerenciamento do
   ciclo de vida.

   Um finalizador é considerado *alive* (vivo) até ser chamado
   (explicitamente ou na coleta de lixo), e depois disso ele é *dead*
   (morto). Chamar um finalizador vivo retorna o resultado da
   avaliação de "func(*arg, **kwargs)", enquanto chamar um finalizador
   morto retorna "None".

   Exceções levantadas por funções de retorno do finalizador durante a
   coleta de lixo serão mostradas na saída de erro padrão, mas não
   podem ser propagadas. Elas são tratadas da mesma forma que exceções
   levantadas de um método "__del__()" de um objeto ou de uma função
   de retorno da referência fraca.

   Quando o programa é encerrado, cada finalizador vivo restante é
   chamado, a menos que seu atributo "atexit" tenha sido definido como
   falso. Eles são chamados na ordem reversa da criação.

   Um finalizador nunca vai invocar sua função de retorno durante a
   parte posterior do *desligamento do interpretador* quando os
   globais do módulo podem ter sido substituídos por "None".

   __call__()

      Se *self* estiver vivo, marca-o como morto e retorna o resultado
      da chamada "func(*args, **kwargs)". Se *self* estiver morto,
      retorna "None".

   detach()

      Se *self* estiver vivo, marca-o como morto e retorna a tupla
      "(obj, func, args, kwargs)". Se *self* estiver morto, retorna
      "None".

   peek()

      Se *self* estiver vivo, retorna a tupla "(obj, func, args,
      kwargs)". Se *self* estiver morto, retorna "None".

   alive

      Propriedade que é verdadeiro se o finalizador estiver ativo,
      falsa caso contrário.

   atexit

      Uma propriedade booleana gravável que por padrão é verdadeiro.
      Quando o programa sai, ele chama todos os finalizadores vivos
      restantes para os quais "atexit" é verdadeiro. Eles são chamados
      na ordem reversa da criação.

   Nota:

     É importante garantir que *func*, *args* e *kwargs* não possuam
     nenhuma referência a *obj*, direta ou indiretamente, pois, caso
     contrário, *obj* nunca será coletado como lixo. Em particular,
     *func* não deve ser um método vinculado de *obj*.

   Adicionado na versão 3.4.

weakref.ReferenceType

   O objeto tipo para objetos referências fracas.

weakref.ProxyType

   O tipo de objeto para intermediários de objetos que não são
   chamáveis.

weakref.CallableProxyType

   O objeto de tipo para intermediários de objetos chamáveis.

weakref.ProxyTypes

   Sequência contendo todos os objetos de tipo para intermediários.
   Isso pode tornar mais simples testar se um objeto é um
   intermediário sem depender da nomeação de ambos os tipos de
   intermediário.

Ver também:

  **PEP 205** - Referências fracas
     A proposta e a justificativa para esse recurso, incluindo links
     para implementações anteriores e informações sobre recursos
     semelhantes em outras linguagens.


Objetos referência fraca
========================

Objetos referência fraca não têm métodos nem atributos além de
"ref.__callback__". Um objeto referência fraca permite que o referente
seja obtido, se ele ainda existir, chamando-o:

>>> import weakref
>>> class Object:
...     pass
...
>>> o = Object()
>>> r = weakref.ref(o)
>>> o2 = r()
>>> o is o2
True

Se o referente não existir mais, chamar o objeto referência retornará
"None":

>>> del o, o2
>>> print(r())
None

O teste de se um objeto referência fraca ainda está vivo deve ser
feito usando a expressão "ref() is not None". Normalmente, o código da
aplicação que precisa usar um objeto de referência deve seguir este
padrão:

   # r é um objeto referência fraca
   o = r()
   if o is None:
       # referente foi coletado como lixo
       print("O objeto foi desalocado; não é possível mexer com ele.")
   else:
       print("O objeto ainda está vivo!")
       o.faz_algo_útil()

Usar um teste separado para "vivacidade" cria condições de corrida em
aplicações que usam mais de uma thread; uma outra thread pode fazer
com que uma referência fraca seja invalidada antes que a referência
fraca seja chamada; o idioma mostrado acima é seguro em aplicações que
usam mais de uma thread, bem como em aplicações de thread única.

Versões especializadas de objetos "ref" podem ser criadas por meio de
subclasse. Isso é usado na implementação do "WeakValueDictionary" para
reduzir a sobrecarga de memória para cada entrada no mapeamento. Isso
pode ser mais útil para associar informações adicionais a uma
referência, mas também pode ser usado para inserir processamento
adicional em chamadas para recuperar o referente.

Este exemplo mostra como uma subclasse de "ref" pode ser usada para
armazenar informações adicionais sobre um objeto e afetar o valor
retornado quando o referente é acessado:

   import weakref

   class ExtendedRef(weakref.ref):
       def __init__(self, ob, callback=None, /, **annotations):
           super().__init__(ob, callback)
           self.__counter = 0
           for k, v in annotations.items():
               setattr(self, k, v)

       def __call__(self):
           """Retorna um par contendo o referente e o número de
           vezes que a referência foi chamada.
           """
           ob = super().__call__()
           if ob is not None:
               self.__counter += 1
               ob = (ob, self.__counter)
           return ob


Exemplo
=======

Este exemplo simples mostra como uma aplicação pode usar IDs de objeto
para recuperar objetos que ele viu antes. Os IDs dos objetos podem
então ser usados em outras estruturas de dados sem forçar os objetos a
permanecerem vivos, mas os objetos ainda podem ser recuperados por ID
se o fizerem.

   import weakref

   _id2obj_dict = weakref.WeakValueDictionary()

   def remember(obj):
       oid = id(obj)
       _id2obj_dict[oid] = obj
       return oid

   def id2obj(oid):
       return _id2obj_dict[oid]


Objetos finalizadores
=====================

O principal benefício de usar "finalize" é que ele simplifica o
registro de um retorno de chamada sem precisar preservar o objeto
finalizador retornado. Por exemplo

>>> import weakref
>>> class Object:
...     pass
...
>>> kenny = Object()
>>> weakref.finalize(kenny, print, "You killed Kenny!")
<finalize object at ...; for 'Object' at ...>
>>> del kenny
You killed Kenny!

O finalizador pode ser chamado diretamente também. No entanto, o
finalizador vai invocar a função de retorno no máximo uma vez.

>>> def callback(x, y, z):
...     print("CALLBACK")
...     return x + y + z
...
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> assert f.alive
>>> assert f() == 6
CALLBACK
>>> assert not f.alive
>>> f()                     # callback not called because finalizer dead
>>> del obj                 # callback not called because finalizer dead

Você pode desfazer o registro de um finalizador usando seu método
"detach()". Isso elimina o finalizador e retorna os argumentos
passados ao construtor quando ele foi criado.

>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()
(<...Object object ...>, <function callback ...>, (1, 2), {'z': 3})
>>> newobj, func, args, kwargs = _
>>> assert not f.alive
>>> assert newobj is obj
>>> assert func(*args, **kwargs) == 6
CALLBACK

A menos que você defina o atributo "atexit" como "False", um
finalizador será chamado quando o programa sair se ele ainda estiver
vivo. Por exemplo

   >>> obj = Object()
   >>> weakref.finalize(obj, print, "obj dead or exiting")
   <finalize object at ...; for 'Object' at ...>
   >>> exit()
   obj dead or exiting


Comparando finalizadores com métodos "__del__()"
================================================

Suponha que queremos criar uma classe cujas instâncias representam
diretórios temporários. Os diretórios devem ser excluídos com seus
conteúdos quando o primeiro dos seguintes eventos ocorrer:

* o objeto é um lixo coletado,

* o método "remove()" do objeto é chamado, ou

* o programa finaliza.

Podemos tentar implementar a classe usando um método "__del__()" da
seguinte maneira:

   class TempDir:
       def __init__(self):
           self.name = tempfile.mkdtemp()

       def remove(self):
           if self.name is not None:
               shutil.rmtree(self.name)
               self.name = None

       @property
       def removed(self):
           return self.name is None

       def __del__(self):
           self.remove()

A partir do Python 3.4, os métodos "__del__()" não impedem mais que os
ciclos de referência sejam coletados como lixo, e os módulos globais
não são mais forçados a "None" durante *desligamento do
interpretador*. Então, esse código deve funcionar sem problemas no
CPython.

Entretanto, o tratamento dos métodos "__del__()" é notoriamente
específico da implementação, pois depende de detalhes internos da
implementação do coletor de lixo do interpretador.

Uma alternativa mais robusta pode ser definir um finalizador que faça
referência apenas às funções e objetos específicos de que necessita,
em vez de ter acesso ao estado completo do objeto:

   class TempDir:
       def __init__(self):
           self.name = tempfile.mkdtemp()
           self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)

       def remove(self):
           self._finalizer()

       @property
       def removed(self):
           return not self._finalizer.alive

Definido assim, nosso finalizador recebe apenas uma referência aos
detalhes que ele precisa para limpar o diretório apropriadamente. Se o
objeto nunca for coletado como lixo, o finalizador ainda será chamado
na saída.

A outra vantagem dos finalizadores baseados em referências fracas é
que eles podem ser usados para registrar finalizadores para classes
onde a definição é controlada por terceiros, como executar código
quando um módulo é descarregado:

   import weakref, sys
   def unloading_module():
       # referência implícita aos globais do módulo do corpo da função
   weakref.finalize(sys.modules[__name__], unloading_module)

Nota:

  Se você criar um objeto finalizador em uma thread em um daemon assim
  que o programa sair, então há a possibilidade de que o finalizador
  não seja chamado na saída. No entanto, em um thread em um daemon
  "atexit.register()", "try: ... finally: ..." e "with: ..." não
  garantem que a limpeza ocorra também.
