col*_*fix 5 python memory-leaks weak-references decorator python-decorators
尝试修改装饰器不使用a weakref
,我偶然发现了以下行为:
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!!!
Run Code Online (Sandbox Code Playgroud)
这对我来说似乎很奇怪,因为我希望两种情况都表现得一样.此外,根据我对引用计数和对象生命周期的理解,我确信在这两种情况下都应该释放对象.
我在python2.7和python3.3上测试了它.
这是一个错误还是故意行为?有没有办法让两个调用都有预期的结果(释放有问题的对象)?
我不想使用weakref
in,proxy
因为这会破坏绑定方法的正确对象生存期语义:
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
Run Code Online (Sandbox Code Playgroud)
这两个代码路径不相同.
就地添加动作对两个操作员,分配目标和添加的项目.在test1
这是temp
,一个局部变量,并就地除了被翻译成以下几点:
temp = temp.__iadd__(object())
Run Code Online (Sandbox Code Playgroud)
并且由于您返回self
并temp
引用同一个对象,因此会temp = temp
在函数退出后清除该引用.
在test2
,你复杂的事情,因为现在描述符再次涉及:
a.descr += object()
Run Code Online (Sandbox Code Playgroud)
变为:
a.descr = A.__dict__['descr'].__get__(a, A).__iadd__(object())
Run Code Online (Sandbox Code Playgroud)
所以你将结果分配给A.__dict__['descr'].__get__(a, A)
实例属性a.descr
; 描述符没有__set__()
方法,也没有参考.
但是,这里是捕获,proxy
对象持有对a
自身a.descr.instance
的引用,是一个引用a
!您创建了循环引用.
这个引用使对象保持足够长的时间以通过弱引用显示,但是一旦垃圾收集过程运行并打破这个循环,a
无论如何都会消失.
这个故事的道德?不要__iadd__
与非数据描述符结合使用; 包括两者__get__
, __set__
因为你需要控制分配结果时会发生什么.
归档时间: |
|
查看次数: |
135 次 |
最近记录: |