WKP*_*lus 11 python python-2.x python-3.x python-internals
我找到了一些有趣的东西,这里有一段代码:
class A(object):
def __init__(self):
print "A init"
def __del__(self):
print "A del"
class B(object):
a = A()
Run Code Online (Sandbox Code Playgroud)
如果我运行此代码,我会得到:
A init
Run Code Online (Sandbox Code Playgroud)
但是,如果我更改class B(object)到class B(),我会得到:
A init
A del
Run Code Online (Sandbox Code Playgroud)
我在__del__ doc中找到了一个注释:
无法保证为解释器退出时仍然存在的对象调用del()方法.
然后,我想这是因为当解释器存在时B.a仍然引用(由类引用B).
所以,我del B在解释器手动存在之前添加了一个,然后我发现它a.__del__()被调用了.
现在,我对此感到有些困惑.为什么a.__del__()在使用旧样式时调用?为什么新旧风格的行为有不同的行为?
我在这里发现了类似的问题,但我认为答案还不够明确.
Ant*_*ala 11
TL; DR:这是CPython中的一个老问题,最终在CPython 3.4中修复.由模块全局变量引用的引用循环保持活动的对象在3.4之前的CPython版本中的解释器出口上未正确完成.新式类在其type实例中具有隐式循环; 旧式类(类型classobj)没有隐式引用循环.
即使在这种情况下修复了,CPython 3.4文档仍然建议不要依赖于__del__在解释器退出时调用 - 请考虑自己警告.
新风格类本身具有参考周期:最值得注意的是
>>> class A(object):
... pass
>>> A.__mro__[0] is A
True
Run Code Online (Sandbox Code Playgroud)
这意味着它们不能立即删除*,但仅在运行垃圾收集器时才会删除.由于主模块正在保存对它们的引用,因此它们将保留在内存中,直到解释器关闭.最后,在模块清理期间,main中的所有模块全局名称都设置为指向None,并且任何对象的引用计数减少到零(例如,旧样式类)也被删除.但是,具有引用周期的新式类将不会由此发布/最终确定.
循环垃圾收集器不会在解释器出口运行(CPython文档允许这样做:
无法保证
__del__()在解释器退出时仍然存在的对象调用方法.
现在,Python 2中的旧式类没有隐式循环.当CPython模块清理/关闭代码将全局变量设置为时None,将B删除对类的唯一剩余引用; 然后B被删除,最后一个引用a被删除,a也完成了.
为了证明新式类具有循环并需要GC扫描这一事实,而旧式类没有,您可以在CPython 2中尝试以下程序(CPython 3不再具有旧式类):
import gc
class A(object):
def __init__(self):
print("A init")
def __del__(self):
print("A del")
class B(object):
a = A()
del B
print("About to execute gc.collect()")
gc.collect()
Run Code Online (Sandbox Code Playgroud)
与B上面的新式类一样,输出是
A init
About to execute gc.collect()
A del
Run Code Online (Sandbox Code Playgroud)
使用B旧式class(class B:),输出是
A init
A del
About to execute gc.collect()
Run Code Online (Sandbox Code Playgroud)
也就是说,gc.collect()即使最后一次外部引用已被删除,新样式类也被删除了; 但旧式的课程立刻被删除了.
其中大部分已在Python 3.4中得到修复:多亏了PEP 442,其中包括基于GC代码的模块关闭程序.现在即使在解释器退出时,模块全局变量也使用普通的垃圾收集来完成.如果在Python 3.4下运行程序,程序将打印出来
A init
A del
Run Code Online (Sandbox Code Playgroud)
而Python <= 3.3则会打印出来
A init
Run Code Online (Sandbox Code Playgroud)
(请注意,此时其他实现仍可能执行或不执行__del__,无论它们的版本高于,高于或低于3.4)
| 归档时间: |
|
| 查看次数: |
279 次 |
| 最近记录: |