Почему __del__ вызывается в конце блока with?

Область действия переменных, созданных в операторе with, находится за пределами блока with (см. -with-block">Переменная, определенная с помощью инструкции with, доступна вне блока with?). Но когда я запускаю следующий код:

class Foo:
    def __init__(self):
        print "__int__() called."

    def __del__(self):
        print "__del__() called."

    def __enter__(self):
        print "__enter__() called."
        return "returned_test_str"

    def __exit__(self, exc, value, tb):
        print "__exit__() called."

    def close(self):
        print "close() called."

    def test(self):
        print "test() called."

if __name__ == "__main__":
    with Foo() as foo:
        print "with block begin???"
        print "with block end???"

    print "foo:", foo  # line 1

    print "-------- Testing MySQLdb -----------------------"
    with MySQLdb.Connect(host="xxxx", port=0, user="xxx", passwd="xxx", db="test") as my_curs2:
        print "(1)my_curs2:", my_curs2
        print "(1)my_curs2.connection:", my_curs2.connection
    print "(2)my_curs2.connection:", my_curs2.connection
    print "(2)my_curs2.connection.open:", my_curs2.connection.open  # line 2

Вывод показывает, что Foo.__del__ вызывается перед печатью foo (в # line 1 выше):

__int__() called.
__enter__() called.
with block begin???
with block end???
__exit__() called.
__del__() called.
foo: returned_test_str
-------- Testing MySQLdb -----------------------
(1)my_curs2: <MySQLdb.cursors.Cursor object at 0x7f16dc95b290>
(1)my_curs2.connection: <_mysql.connection open to 'xxx' at 2609870>
(2)my_curs2.connection: <_mysql.connection open to 'xxx' at 2609870>
(2)my_curs2.connection.open: 1

Мой вопрос: почему здесь вызывается Foo.__del__, если оператор with не создает новую область выполнения?

Кроме того, если метод соединения __del__ вызывается во втором блоке with, я не понимаю, почему my_curs1.connection после этого все еще открыт (см. # line 2 выше).


person BAE    schedule 06.10.2015    source источник
comment
Возможный дубликат Могу ли я использовать оператор with с объектом MySQLdb.Connection?   -  person tzaman    schedule 06.10.2015
comment
Chengcheng, см. ссылку @tzaman для ответа на ваш второй вопрос и удалите эту часть из своего вопроса. Сведение одной проблемы к одному вопросу помогает поддерживать порядок в StackOverflow и позволяет людям быстрее находить ответы. Спасибо!   -  person CodeMouse92    schedule 06.10.2015
comment
@tzaman Этому вопросу 3 года, и ответ на него неверен.   -  person Air    schedule 06.10.2015
comment
Правда - это интересный вопрос. Более актуальное обсуждение диспетчера контекста mysqldb здесь . @air прав - связанный дубликат устарел   -  person J Richard Snape    schedule 06.10.2015
comment
@ChengchengPei Да, я знаю, что это другое. Я пытался предотвратить закрытие вашего вопроса как дубликата, как предлагает @tzaman, потому что ответ устарел. Я не знал о ссылке, которую вы предоставили, до 15 минут назад. Я могу только догадываться, почему __del__ вызывается, но могу сказать, что это вызвало обсуждение в чате Python — chat.stackoverflow. com/комнаты/6/питон   -  person J Richard Snape    schedule 06.10.2015


Ответы (1)


Важно отметить, что foo не является объектом типа Foo. Вы создаете Foo и должны хранить его, потому что он может содержать информацию о состоянии, необходимую для вызова __exit__. Но как только это сделано, объект становится ненужным, и Python может его выбросить.

Другими словами, это:

with Foo() as foo:
    print ('Hello World!')

То же, что:

_bar = Foo()
foo = _bar.__enter__()
print ('Hello World!')
_bar.__exit__()
del _bar # This will call __del__ because _bar is the only reference

Ожидаемое поведение произойдет, если foo будет ссылкой на блок with foo. Например...

class Foo:
    def __init__(self):
        print ("__int__() called.")

    def __del__(self):
        print ("__del__() called.")

    def __enter__(self):
        print ("__enter__() called.")
        return self # foo now stores the Foo() object

    def __str__(self):
        return 'returned_test_str'

    def __exit__(self, exc, value, tb):
        print ("__exit__() called.")

    def close(self):
        print ("close() called.")

    def test(self):
        print ("test() called.")

if __name__ == "__main__":
    with Foo() as foo:
        print ("with block begin???")
        print ("with block end???")

    print ("foo:", foo)  # line 1

Отпечатки

__int__() called.
__enter__() called.
with block begin???
with block end???
__exit__() called.
foo: returned_test_str
__del__() called.

Однако я понятия не имею, почему Connection.__exit__ оставил свои курсоры открытыми.

person QuestionC    schedule 06.10.2015
comment
с close( self.__conn.cursor() ) as cur: вызовет close(), но не __exit__() и __enter__(). Но with-statement вызовет __exit__() и __enter__(), но не close(). См. stackoverflow.com/questions/5669878/ - person BAE; 06.10.2015
comment
Мне просто интересно, почему __del__() вызывается перед оператором печати (строка 1 в моем посте). Для меня это имеет смысл, если __del__() вызывается при выходе из программы. - person BAE; 06.10.2015
comment
__del__ вызывается, потому что больше нет ссылок на временный объект, созданный блоком with. Помните, foo — это не Foo(), которое вы сделали. - person QuestionC; 06.10.2015
comment
Теперь это имеет смысл для меня, если python выполняет gc перед выходом из программы. Спасибо. - person BAE; 06.10.2015
comment
Технически он может делать то, что хочет, но на практике он очищается, как только у вас больше нет ссылок на объект. - person QuestionC; 06.10.2015