2010-03-11 26 views
31

Supongamos que tengo un código que mantiene una estructura padre/hijos. En dicha estructura, obtengo referencias circulares, donde un niño señala a un padre y un padre apunta a un niño. ¿Debería preocuparme por ellos? Estoy usando Python 2.5.¿Debería preocuparme por las referencias circulares en Python?

Me preocupa que no se recojan basura y la aplicación eventualmente consumirá toda la memoria.

Respuesta

25

"preocupación" está fuera de lugar, pero si el programa resulta ser lento, consume más memoria de lo esperado, o tienen extraño inexplicable pausas, la causa es, en efecto probable que sea en esos bucles de referencia de basura - que necesitan para ser basura recogida por un procedimiento diferente que (acíclicos) gráficos de referencia "normales", y que la recolección es ocasional y puede ser lento si usted tiene un montón de objetos atados en tales bucles (la colección de basura cíclica también se inhibe si un objeto en el bucle tiene un método especial __del__).

Por lo tanto, los bucles de referencia no afectarán la corrección de su programa, pero pueden afectar su rendimiento y/o huella.

Si y cuando desea eliminar los bucles de referencias no deseados, a menudo puede utilizar el módulo weakref en la biblioteca estándar de Python.

Si y cuando desea ejercer un control más directo (o realizar la depuración, vea qué está sucediendo exactamente) con respecto a la recolección cíclica de basura, use el módulo gc en la biblioteca estándar de Python.

+0

más 1 para la nota sobre '__del__'. Si los destructores de objetos tienen efectos secundarios, es posible que desee pensar en referencias cíclicas (y cuando las cosas se destruyan) con más cuidado. – speedplane

9

Python detectará el ciclo y liberará la memoria cuando no haya referencias externas.

+0

Suponiendo, por supuesto, no existen métodos de '__del__'. Lo que normalmente no debería ser, pero nunca se sabe. Por un tiempo, incluso 'collections.OrderedDict' tuvo uno por alguna razón. – Antimony

15

Experimentalmente: usted está muy bien:

import itertools 

for i in itertools.count(): 
    a = {} 
    b = {"a":a} 
    a["b"] = b 

mantenerse siempre en el uso de 3,6   MB de RAM.

+2

¡Genial! Entonces estoy a salvo. :) – bodacydo

+0

¿Qué implementación usaste? –

+0

@SargeBorsch CPython 2.algo. Me imagino que cualquiera de las principales implementaciones se comportaría de la misma manera. – cobbal

5

Las referencias circulares son algo normal de hacer, por lo que no veo motivo para preocuparme por ellas. Muchos algoritmos de árbol requieren que cada nodo tenga enlaces a sus elementos secundarios y sus elementos principales. También se les exige implementar algo así como una lista doblemente vinculada.

+0

Gracias Colin. No sabía que eran "algo normal". Me parecieron muy especiales. Pero ahora aprendí lo contrario. :) – bodacydo

+0

Además, obviamente son necesarios para los gráficos. – Antimony

3

No creo que deba preocuparse. Pruebe el siguiente programa y va a ver que no va a consumir toda la memoria:

while True: 
    a=range(100) 
    b=range(100) 
    a.append(b) 
    b.append(a) 
    a.append(a) 
    b.append(b) 
+0

Gracias por escribir este código de prueba. No pensé en eso. – bodacydo

+0

¿no quiere decir 'a.extend (b)', no 'append'? – richizy

+4

@richizy Realmente me refiero a append porque quiero guardar la referencia a a y b dentro de a y b, no los valores. De esta manera, la referencia circular sucederá. – douglaz

1

Parece que hay un problema con las referencias a los métodos en las listas de una variable. Aquí hay dos ejemplos. El primero no llama al __del__. El segundo con weakref está bien para __del__. Sin embargo, en este último caso el problema es que no se puede hacer referencia débilmente métodos: http://docs.python.org/2/library/weakref.html

import sys, weakref 

class One(): 
    def __init__(self): 
     self.counters = [ self.count ] 
    def __del__(self): 
     print("__del__ called") 
    def count(self): 
     print(sys.getrefcount(self)) 


sys.getrefcount(One) 
one = One() 
sys.getrefcount(One) 
del one 
sys.getrefcount(One) 


class Two(): 
    def __init__(self): 
     self.counters = [ weakref.ref(self.count) ] 
    def __del__(self): 
     print("__del__ called") 
    def count(self): 
     print(sys.getrefcount(self)) 


sys.getrefcount(Two) 
two = Two() 
sys.getrefcount(Two) 
del two 
sys.getrefcount(Two) 
Cuestiones relacionadas