Patrón de delegado de Python – ¿Cómo evitar la referencia circular?

Me gustaría preguntar si usar el Patrón de Delegado en Python llevaría a referencias circulares y, de ser así, ¿cuál sería la mejor manera de implementarlo para garantizar que el objeto y su delegado se recolecten como basura?

En el Objetivo C, el problema anterior se evita utilizando una referencia débil al delegado. En C ++, no llamamos a eliminar en el delegado. He encontrado un enlace al módulo de referencia débil de Python aquí: http://docs.python.org/library/weakref.html . Parece que un enfoque plausible podría ser crear una referencia débil para referirse a la variable de instancia mediante este módulo, pero no estoy seguro.

Como he buscado en Google esta pregunta y no pude encontrar respuestas, me pregunto si esto es un problema en Python o si hay una solución común (sin la necesidad del módulo weakref) que desconozco. ¿de? Además, hice búsquedas en stackoverflow antes de hacer las preguntas, pero las preguntas que encontré trataban de importaciones circulares o patrones de delegado en general y no específicos de Python y el problema de las referencias circulares.

Gracias de antemano por cualquier respuesta.

A continuación se incluye un código para un ejemplo de juguete que ayuda a ilustrar mi pregunta. He implementado el código de esta manera y funciona, pero no estoy seguro de si la memoria se recolecta al final.

class A(object): def __init__(self): self.delegate = None # Some other instance variables that keep track of state for performing some tasks. def doSomething(self): if self.delegate is not None: self.delegate.doSomething() else: print('Cannot perform task because delegate is not set.') # Other methods not shown. class B(object): def __init__(self): self.a = A() # Need to keep object 'a' from garbage collected so as to preserve its state information. self.a.delegate = self # Is this a circular reference? How to 'fix' it so that A and B will eventually be garbage collected? def doSomething(self): print('B doing something') # Other methods not shown. 

EDITAR :

Después de leer algunas de las respuestas, decidí aclarar mi pregunta. Entiendo que Python tiene recolección de basura. Lo que no estaba seguro era si realizaría la recolección de basura en objetos circulares referenciados. Mis preocupaciones provienen del siguiente pasaje del documento de Python:

Detalle de la implementación de CPython: CPython actualmente utiliza un esquema de conteo de referencias con (opcional) detección retardada de basura enlazada cíclicamente, que recolecta la mayoría de los objetos tan pronto como se vuelven inalcanzables, pero no se garantiza que recolecte basura que contenga referencias circulares . Consulte la documentación del módulo gc para obtener información sobre cómo controlar la recolección de basura cíclica. Otras implementaciones actúan de manera diferente y CPython puede cambiar. No dependa de la finalización inmediata de los objetos cuando se vuelven inalcanzables (por ejemplo, siempre cierre los archivos).

El pasaje en su forma original se puede encontrar aquí: http://docs.python.org/reference/datamodel.html La configuración en negrita es mía.

La siguiente publicación proporciona una explicación más clara sobre el problema de los objetos circulares referenciados y por qué evitaría la recolección de basura en esos objetos (al menos en una configuración típica): http://www.electricmonk.nl/log/2008/07/07 / python-destructor-and-garbage-collection-notes / .

Además, acabo de encontrar la respuesta de Alex Martellli a la siguiente pregunta sobre si los usuarios de Python deberían preocuparse por la referencia circular: ¿Debería preocuparme por las referencias circulares en Python? De su respuesta, deduzco que a pesar de que los objetos circulares de referencia eventualmente serán recolectados como basura, PERO habrá gastos indirectos. Si es significativo depende del progtwig.

Además, mencionó usar el módulo de referencia débil de Python, pero no dijo explícitamente cómo.

Por lo tanto, me gustaría agregar las siguientes preguntas para aclarar algunos problemas sin resolver:

  1. Los documentos dicen que la colección garbaged no está garantizada para los objetos de referencia circulares. Pero de las respuestas parece que no es el caso. Así que, ¿he entendido mal el pasaje o hay más detalles que me he perdido?
  2. Supongo que usar una referencia débil, como se indica en la respuesta de Alex y mi pregunta, ¿evitaría la sobrecarga del problema por completo?

Una vez más, gracias por las respuestas.

Python ya hace recolección de basura. Solo necesita hacer algo especial si escribe sus propios tipos de contenedor en C, como extensiones.

Demostración: ejecute este progtwig y observe cómo aumenta el uso de la memoria.

 class C(object): pass def circular(): for x in range(10**4): for y in range(10**4): a = C() b = C() ax = b bx = a circular() 

Nota al pie: la siguiente función no hace nada, elimínela.

 def setDelegate(self, delegate): self.delegate = delegate 

En lugar de llamar a x.setDelegate(y) , puede usar x.delegate = y . Puede sobrecargar el acceso de miembros en Python, por lo que no hay beneficios al escribir un método.

¿Por qué no sería basura recogida al final? Cuando el script haya terminado y Python complete la ejecución, toda la sección de la memoria se marcará para la recolección de basura y (eventualmente) la recuperación del sistema operativo.

Si está ejecutando esto en un progtwig de ejecución prolongada, una vez que A y B estén sin referencia, la memoria será reclamada.