为什么在with块的末尾调用__del__?

BAE*_*BAE 9 python with-statement mysql-python

with语句中创建的变量的范围在with块之外(请参阅:使用with-statement在with-block之外可用的变量定义?).但是当我运行以下代码时:

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
Run Code Online (Sandbox Code Playgroud)

输出显示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
Run Code Online (Sandbox Code Playgroud)

我的问题是,Foo.__del__如果with声明没有创建新的执行范围,为什么在这里调用?

此外,如果__del__在第二个with块中调用连接的方法,我不明白为什么my_curs1.connection之后仍然打开(见# line 2上文).

Que*_*onC 7

重要的是要注意它foo不是类型的对象Foo。您确实创建了Foo并且需要保留它,因为它可能包含调用__exit__. 但是一旦完成,对象就不再需要了,Python 可以随意丢弃它。

换句话说,这个:

with Foo() as foo:
    print ('Hello World!')
Run Code Online (Sandbox Code Playgroud)

是不是和这个一样:

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

如果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
Run Code Online (Sandbox Code Playgroud)

印刷

__int__() called.
__enter__() called.
with block begin???
with block end???
__exit__() called.
foo: returned_test_str
__del__() called.
Run Code Online (Sandbox Code Playgroud)

但是,我不知道为什么Connection.__exit__要打开它的游标。