С оператором, автоудаление объекта
Можно ли удалить форму объекта внутри своего класса?
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
Выше также указано, как он обрабатывается для обработчика файлов.