带语句,自动删除对象

时间:2018-09-28 08:39:05

标签: python with-statement object-destruction

是否可以在其类内删除对象形式?

class A():
    def __init__(self):
        print("init")
        self.b="c"
    def __enter__(self):
        print("enter")
        return self
    def __exit__(self, type, value, traceback):
        print("exit")      

with A() as a:
    print(a.b)
print(a.b)

返回:

init
enter
c
exit
c

为什么退出a后仍然可以访问with对象?有没有一种方法可以自动删除__exit__中的对象?

3 个答案:

答案 0 :(得分:5)

是,不是。在del a子句之后使用with。这将删除变量a,它是对象上的最后一个引用持有者。

对象本身(即__exit__()中的对象)不能使知道它并持有引用(即with子句中的代码)的人忘记这一点。只要引用存在,对象就会存在。

当然,您的对象可以在__exit__()中清空自己,并保留为空心对象(例如,在这种情况下为del self.b)。

答案 1 :(得分:2)

class A():
    def __init__(self):
        print("init")
        self.b="c"
    def __enter__(self):
        print("enter")
        return self
    def __exit__(self, type, value, traceback):
        print("exit") 
        del self.b

with A() as a:
    print(a.b)
print(a.b)

您不能在__exit__中删除类A本身的实例。最好的办法是删除属性b

init
enter
c
exit
Traceback (most recent call last):
  File "main.py", line 14, in <module>
    print(a.b)
AttributeError: A instance has no attribute 'b'

答案 2 :(得分:2)

简短的答案 :(在某种程度上)可能,但根本不建议。

Python中的with部分具有 no 专用范围,因此这意味着不会删除with语句中在 中定义的变量。这是经常要的行为。例如,如果您加载文件,则可以这样写:

with open('foo.txt') as f:
    data = list(f)

print(data)

您不希望删除data变量:with用于确保文件处理程序正确关闭(并且如果处理程序主体中发生异常,则处理程序也将关闭) with)。

严格来说,您可以使用“骇人听闻的”解决方案删除引用A()对象的局部变量:我们检查调用堆栈,并删除对self的引用(或其他对象),例如:

import inspect

class A(object):

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        locs = inspect.stack()[1][0].f_locals
        ks = [k for k, v in locs.items() if v is self]
        for k in ks:
            del locs[k]

然后它将删除它,就像:

>>> with A() as a:
...   pass
...
>>> a
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined 

但我会强烈对此提出建议。首先,如果可变参数是全局的,或者位于局部范围之外,则不会在此处将其删除(我们可以修复此问题,但会引入很多额外的逻辑)。

此外,并不是说变量甚至存在,如果变量是可迭代的,则可以像下面这样定义它:

# If A.__enter__ returns an iterable with two elements

with A() as (foo, bar):
    pass

因此这些元素将不会被回收。最后,如果__enter__返回self,则有可能“删除过多”,因为一个人可以写with foo as bar,然后写foobar将被删除。

无论如何,大多数IDE可能仍无法理解__exit__中的逻辑,因此自动完成后仍将包含a

通常,最好将对象标记为已关闭,例如:

import inspect

class A(object):

    def __init__(self):
        self.closed = False

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.closed = True

    def some_method(self):
        if self.closed:
            raise Exception('A object is closed')
        # process request

以上也是文件处理程序的处理方式。