С оператором, автоудаление объекта - PullRequest
0 голосов
/ 28 сентября 2018

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

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 голосов
/ 28 сентября 2018
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'
0 голосов
/ 28 сентября 2018

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

Часть 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:
            <b>del locs[k]</b>

Затем он удаляет его следующим образом:

>>> 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

Выше также описывается способ обработки файлового обработчика.

0 голосов
/ 28 сентября 2018

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

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

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

...