2012-06-03 14 views
5

Este es un intento de comprender mejor cómo funciona el recuento de referencias en Python.Descripción del recuento de referencias de la variable de clase

Creemos una clase y creemos una instancia. contador de referencia del ejemplo sería 1 (getrefcount pantallas 2 porque las propias estructuras internas hacen referencia a esa instancia de la clase aumentar el conteo de referencia por 1):

>>> from sys import getrefcount as grc 
>>> class A(): 
    def __init__(self): 
     self.x = 100000 


>>> a = A() 
>>> grc(a) 
2 

a 's internos variables x tiene 2 referencias:

>>> grc(a.x) 
3 

Esperaba que fuera referenciado por a y por el método A__init__. Entonces decidí verificar.

Así que creé una variable temporal b en el espacio de nombres __main__ solo para poder acceder a la variable x. Aumentó el número de ref por 1 para que se convierta 3 (como se esperaba):

>>> b = a.x 
>>> grc(a.x) 
4 

Entonces suprime la instancia de clase y el recuento ref disminuido en 1:

>>> del a 
>>> grc(b) 
3 

Así que ahora hay 2 referencias: una es por b y la otra es por A (como esperaba).

Al eliminar A desde __main__ espacio de nombres Espero que el conteo disminuya en 1 nuevamente.

>>> del A 
>>> grc(b) 
3 

Pero no sucede. No existe ninguna clase A o sus instancias que puedan hacer referencia a 100000, pero aún así se hace referencia a ella en el espacio de nombres .

Entonces, mi pregunta es, ¿a qué se refiere 100000 aparte de b?


BrenBarn sugirió que debería utilizar object() lugar de un número que puede ser almacenado en algún lugar internamente.

>>> class A(): 
    def __init__(self): 
     self.x = object() 


>>> a = A() 
>>> b = a.x 
>>> grc(a.x) 
3 
>>> del a 
>>> grc(b) 
2 

Después de eliminar la instancia a hubiera una sola referencia por b que es muy lógico.

Lo único que queda por entender es por qué no es así con el número 100000.

+0

Supongo que, 'A .__ dict__'? –

+0

@JakobBowyer Pero cuando elimino 'A', entonces' A .__ dict__' debería ser basura recolectada porque no está referenciada por nada (según tengo entendido). – ovgolovin

+1

Ver esta respuesta: http://stackoverflow.com/questions/759740/unexpected-result-from-sys-getrefcount –

Respuesta

2

a.x es el número entero 10000. Esta constante se referencia por el objeto de código correspondiente al método __init__() de A. objetos de código siempre incluyen referencias a todas las constantes literales en el código:

>>> def f(): return 10000 
>>> f.__code__.co_consts 
(None, 10000) 

la línea

del A 

sólo elimina el nombre A y disminuye el contador de referencias de A. En Python 3.x (pero no en 2.x), las clases suelen incluir algunas referencias cíclicas y, por lo tanto, solo se recopilan elementos no utilizados cuando se ejecuta explícitamente el recolector de elementos no utilizados. Y de hecho, el uso de

import gc 
gc.collect() 

después del A da lugar a la reducción de la cuenta de referencia de b.

+0

No debería eliminar el cable 'A' para eliminar' __init__' ya que solo está referenciado por 'A' (según tengo entendido). – ovgolovin

+0

Oh. Ya lo veo. El recuento de referencias de 'A' es' 4' antes de borrar de '__main__'. Entonces esta eliminación solo la reducirá en '1'. ¿Cuáles son los otros objetos que hacen referencia a 'A'? – ovgolovin

+0

@ovgolovin: Me acabo de dar cuenta de que no puedo reproducir sus resultados. Para mí, el clas 'A' en realidad * does * se elimina, y el recuento de referencia de' b' * does * disminuye. La única otra cosa que puedo pensar es: ten cuidado con el '_' del intérprete interactivo. En caso de duda, mejor hacer experimentos con recuentos de referencia en un guión en lugar de hacerlo en el intérprete interactivo. –

2

Es probable que esto sea un artefacto de que use un número entero como valor de prueba. Python a veces almacena objetos enteros para su posterior reutilización, porque son inmutables. Cuando ejecuto su código usando self.x = object() en su lugar (que siempre creará un objeto nuevo para x) obtengo grc(b)==2 al final.

+0

¡Tienes razón! Al cambiar '100000' a' object() 'se modifican los números devueltos a' getrefcount'. Actualizaré la pregunta. – ovgolovin

+0

@ovgolovin: Por supuesto que sí, porque el objeto de código solo puede hacer referencia a * constantes * que aparecen en el código. Sin embargo, esto no tiene ninguna relación con la reutilización de objetos enteros. Los enteros no se almacenan aleatoriamente para su posterior reutilización. Python solo contiene un caché de enteros pequeños (generalmente de -5 a 256), que se crean al inicio del intérprete y se usan cuando es necesario. Todos los demás enteros se crean a pedido y nunca se vuelven a usar. –

+0

Downvoting: Si bien la observación es precisa, la razón dada en esta respuesta es incorrecta. –

Cuestiones relacionadas