С оператором, автоудаление объекта

Можно ли удалить форму объекта внутри своего класса?

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 ответа

Решение
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)

Вы не можете удалить экземпляр самого класса A внутри __exit__, Лучшее, что вы можете сделать, это удалить свойство 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'

И да и нет. использование del a после with пункт. Это удалит переменную a кто последний владелец ссылки на объект.

Сам объект (т.е. в __exit__()) не могут сделать те, кто знает об этом и держит ссылку (т.е. код на with оговорка) забудь об этом. Пока ссылка существует, объект будет существовать.

Конечно, ваш объект может опустошить себя в __exit__() и оставаться в пустоте (например, del self.b в этом случае).

Короткий ответ: это (в некоторой степени) возможно, но не рекомендуется вообще.

with часть в Python не имеет выделенной области видимости, так что это означает, что переменные, определенные в 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и тогда оба foo а также bar будут удалены

Большинство 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

Выше также указано, как он обрабатывается для обработчика файлов.

Другие вопросы по тегам