gpt4 book ai didi

python - 在不使用临时文件的情况下通过 __get__ 调用 __iadd__ 时发生内存泄漏

转载 作者:太空狗 更新时间:2023-10-30 01:08:57 25 4
gpt4 key购买 nike

尝试修 retrofit 饰器以使用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!!!

这对我来说似乎很奇怪,因为我希望这两种情况的行为相同。此外,根据我对引用计数和对象生命周期的理解,我确信在这两种情况下都应该释放对象。

我在python2.7和python3.3上都测试过了。

这是错误还是故意行为?有没有办法让两次调用都获得预期的结果(释放有问题的对象)?

我不想在 proxy 中使用 weakref,因为这会破坏绑定(bind)方法的正确对象生命周期语义:

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

最佳答案

这两个代码路径不等同。

就地添加作用于两个 运算符,赋值目标和添加的项目。在 test1 中,即 temp,一个局部变量,就地添加被翻译成如下:

temp = temp.__iadd__(object())

并且由于您返回 self 并且 temp 指的是同一个对象,这变成了 temp = temp 并且该引用在函数退出。

test2 中,你把事情复杂化了,因为现在又涉及到了描述符:

a.descr += object() 

变成:

a.descr = A.__dict__['descr'].__get__(a, A).__iadd__(object())

因此您将 A.__dict__['descr'].__get__(a, A) 的结果分配给实例属性 a.descr;描述符没有 __set__() 方法并且未被查询。

但是,这里有一个问题,proxy 对象持有对a 本身的引用,a.descr.instance 是对 a 的引用! 您创建了一个循环引用

这个引用使对象存活的时间足够长以通过弱引用显示出来,但是一旦垃圾收集过程运行并打破这个循环,a 无论如何都会消失。

这个故事的寓意是什么?不要将 __iadd__ 与非数据描述符结合使用;包括 __get__ __set__,因为您需要控制返回结果时发生的情况。

关于python - 在不使用临时文件的情况下通过 __get__ 调用 __iadd__ 时发生内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18287336/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com