Python self=None做什么?

Python self=None做什么?,python,garbage-collection,python-internals,python-asyncio,Python,Garbage Collection,Python Internals,Python Asyncio,我在看书。请注意,在方法的末尾有一个self=None语句。它有什么作用 def _run(self): try: self._callback(*self._args) except Exception as exc: msg = 'Exception in callback {}{!r}'.format(self._callback, sel

我在看书。请注意,在方法的末尾有一个
self=None
语句。它有什么作用

def _run(self):
    try:
        self._callback(*self._args)
    except Exception as exc:
        msg = 'Exception in callback {}{!r}'.format(self._callback,
                                                    self._args)
        self._loop.call_exception_handler({
            'message': msg,
            'exception': exc,
            'handle': self,
        })
    self = None  # Needed to break cycles when an exception occurs.
我原以为它会删除该实例,但下面的测试不建议这样做:

class K:
    def haha(self):
        self = None

a = K()
a.haha()
print(a) # a is still an instance

它只是清除对
self
的本地引用,确保如果发生异常,传递给
self的引用。\u loop.call\u exception\u handler()
是唯一剩余的引用,并且没有创建循环

这里仍然需要这样做,因为异常回溯引用了本地名称空间;当函数退出时,不会被清除,因为引用的局部变量仍然存在

这记录在中,并带有警告:

警告:将回溯返回值指定给处理异常的函数中的局部变量将导致循环引用。这将防止同一函数中的局部变量或回溯引用的任何内容被垃圾收集。由于大多数函数不需要访问回溯,因此最好的解决方案是使用类似于
exctype,value=sys.exc_info()[:2]
的方法来仅提取异常类型和值。如果确实需要回溯,请确保在使用后将其删除(最好使用
try…finally
语句),或者在本身不处理异常的函数中调用
exc_info()

因为
tulip
处理程序构成了一个基本的框架类,所以代码通过从本地名称空间中删除
self
来处理回溯循环引用情况,因为它不能保证
\u callback
call\u exception\u handler
函数将清除它们的引用

在CPython中,当对象的引用计数下降到0时,对象将被销毁,但循环引用(一系列对象在一个循环中引用自己)永远不会看到其引用计数下降到0。垃圾收集器确实试图打破这种循环,但它不能总是这样做或速度不够快。显式清除引用可避免创建循环

例如,如果存在
\uuu del\uu
方法,垃圾收集器将不会中断循环,因为它不知道在这种情况下安全中断循环的顺序


即使没有
\uuuu del\uuuu
方法(框架类永远不应该假设这种情况不会发生),最好不要依赖垃圾收集器最终清除周期。

注意,这一行是由Guido引入的

在此版本中,与
\u run
相对应的功能是:

tulip\u log
只是一个普通的记录器:
logging.getLogger(“tulip”)

在引擎盖下,
Logger.exception
sys.exc_info()
的结果存储在
LogRecord
中,但在调用
exception
后,记录对象不会持久存在

为了验证
logging.exception
不会导致引用循环,我做了以下实验:

import time

import logging

class T:
    def __del__(self):
        print('T.__del__ called')

    def test(self):
        try:
            1 / 0
        except Exception:
            logging.exception("Testing")


def run():
    t = T()
    t.test()
    # t is supposed to be garbaged collected


run()

time.sleep(10) # to emulate a long running process
结果是:

$ python test.py 
ERROR:root:Testing
Traceback (most recent call last):
  File "test.py", line 11, in test
    1 / 0
ZeroDivisionError: integer division or modulo by zero
T.__del__ called
对象
t
按预期进行垃圾收集


因此,我认为这里不需要分配
self=None

也许问题应该是“为什么可以将
self
设置为
None
中断周期?什么周期?”你能举一个这样的循环引用的例子吗?我仍然不明白。。。我的意思是,如果语句
self=None
被命中,那么很明显,方法的结尾将被命中,在这种情况下,本地引用将退出范围,那么为什么我们需要
self=None
?@msvalkon:任何直接或间接引用对象的操作都可以
self.ref=self
是最简单的例子。@MartijnPieters我不明白,是不是还有一个圆圈,也就是说,
self
->
\u loop
->
self
,在我们从
locals()
中删除对
self
的引用之后的事件,但至少,
self->call\u exception\u handler->exc->\u traceback\u\u->f\u locals->self
循环消失了。相同的提交在多个位置添加了
self=None
,因为框架这样做是很好的实践,并不是因为代码库单独显示循环引用。@MartijnPieters您能给我举一个生成循环引用的例子吗?可能是通过
\u回调
?我还没有研究过tulip/asyncio库;我不知道设计意图,也不知道您所研究的提交是否是一项正在进行的工作(例如,即使在当时还没有完整的库)。而
日志记录
库允许您注册自定义处理程序和格式化程序;不要只使用默认的代码库。不幸的是,我现在没有时间构建一个示例案例ATM(此评论来自3G连接上的智能手机)。
$ python test.py 
ERROR:root:Testing
Traceback (most recent call last):
  File "test.py", line 11, in test
    1 / 0
ZeroDivisionError: integer division or modulo by zero
T.__del__ called