cercando di modificare un non decoratore utilizzare un weakref
, mi sono imbattuto nel seguente comportamento:Perdita di memoria quando si richiama __iadd__ via __get__ senza l'utilizzo temporaneo
import weakref
class descriptor(object):
def __get__(self, instance, owner):
return proxy(instance)
class proxy(object):
def __init__(self, instance):
self.instance = instance
def __iadd__(self, other):
return self
class A(object):
descr = descriptor()
def is_leaky(test_fn):
a = A()
wr = weakref.ref(a)
test_fn(a)
del a
return wr() is not None
def test1(a):
tmp = a.descr
tmp += object()
def test2(a):
a.descr += object()
print(is_leaky(test1)) # gives False
print(is_leaky(test2)) # gives True!!!
Questo sembra molto strano per me, come avevo aspettarsi che entrambi i casi si comportino allo stesso modo. Inoltre, dalla mia comprensione del conteggio dei riferimenti e della durata dell'oggetto, ero convinto che in entrambi i casi l'oggetto dovrebbe essere liberato.
L'ho provato sia su python2.7 che su python3.3.
Si tratta di un bug o di un comportamento intenzionale? C'è un modo per ottenere che entrambe le chiamate abbiano i risultati attesi (liberare l'oggetto in questione)?
Non voglio usare un weakref
in proxy
perché questo distrugge la corretta semantica di oggetti a vita per i metodi bound:
a = A()
descr = a.descr
del a # a is kept alive since descr is a bound method to a
descr() # should execute a.descr() as expected
Infatti, aggiungendo 'gc.collect()' a 'is_leaky' interrompe il riferimento circolare e causa' is_leaky (test2) 'per restituire False. – unutbu
Sì, cambiando test2 per avere a.descr .__ iadd __ (oggetto()) risulta un secondo Falso. – BartoszKP
WOW. Questo è sottile. L'aggiunta di un NOP '__set__' al descrittore risolve il problema. Grazie mille! – coldfix