Подтвердить что ты не робот

Что делает сам = Нет?

Я читаю исходный код входящего пакета asyncio. Обратите внимание, что в конце метода существует оператор 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
4b9b3361

Ответ 1

Он просто очищает локальную ссылку до self, убедившись, что если возникает исключение, ссылка, переданная в self._loop.call_exception_handler(), является единственной оставшейся ссылкой, и цикл не был создан.

Это все еще необходимо здесь, потому что на локальное пространство имен ссылается на трассировку исключения; он будет не очищаться, когда функция завершается, так как есть ссылка на живые живые.

Это описано в sys.exc_info() документации по функциям с предупреждением:

Предупреждение. Присвоение возвращаемому значению traceback локальной переменной в функции, обрабатывающей исключение, вызовет циклическую ссылку. Это предотвратит что-либо, на которое ссылается локальная переменная в той же функции, или трассировка от сбора мусора. Поскольку большинство функций не требуют доступа к трассировке, лучшим решением является использование чего-то типа exctype, value = sys.exc_info()[:2] для извлечения только типа и значения исключения. Если вам нужна трассировка, обязательно удалите ее после использования (лучше всего сделать с инструкцией try ... finally) или вызовите exc_info() в функции, которая сама не обрабатывает исключение.

Поскольку обработчики tulip образуют основной класс фреймворка, код обрабатывает круглый ссылочный случай трассировки, удаляя self из локального пространства имен, так как он не может гарантировать, что функции _callback или call_exception_handler очистят их ссылки.

В CPython объекты уничтожаются, когда их счетчик ссылок падает до 0, но циклическая ссылка (серия объектов, ссылающихся на себя в цикле) никогда не увидит, что их количество отсчетов ссылок равно 0. Сборщик мусора пытается разбить такие но он не всегда может сделать это или не достаточно быстро. Явная очистка ссылок позволяет избежать циклов.

Например, если существует метод __del__, сборщик мусора не будет прерывать цикл, поскольку он не будет знать, в каком порядке безопасно сломать цикл в этом случае.

Даже если не существует метода __del__ (который класс каркаса никогда не должен принимать, это не так), лучше не полагаться на сборщик мусора в конечном итоге на очистку циклов.

Ответ 2

Обратите внимание, что эта строка вводится в версия 496 от Guido.

В этой ревизии функция, соответствующая _run, run:

def run(self):
    try:
        self._callback(*self._args)
    except Exception:
        tulip_log.exception('Exception in callback %s %r',
                            self._callback, self._args)
    self = None  # Needed to break cycles when an exception occurs.

tulip_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.