Python «с» не удаляет объект

Попытка правильно удалить объект Python. Я создаю объект, а затем предположительно удаляю его с помощью оператора with. Но когда я распечатываю после закрытия оператора with... объект все еще существует:

class Things(object):
   def __init__(self, clothes, food, money):
       self.clothes = clothes
       self.food = food
       self.money = money

   def __enter__(self):
       return self

   def __exit__(self, exc_type, exc_val, exc_tb):
       print('object deleted')

with Things('socks','food',12) as stuff:
    greg = stuff.clothes
    print(greg)


print(stuff.clothes)

возвращает:

socks
object deleted
socks

person rikkitikkitumbo    schedule 15.03.2016    source источник
comment
Ответ Алекса Тейлора прямо на деньгах. Я хочу добавить, что, поскольку Python имеет автоматическое управление памятью (сборку мусора), вам не нужно беспокоиться об удалении объектов, которые больше не используются. Поэтому использование менеджера контекста для этой цели бессмысленно. Также переменная с именем stuff создается оператором with, но, как вы заметили, продолжает существовать до конца скрипта. Если у вас есть строка del stuff, тогда материал имени становится неопределенным. Это редко приходится делать.   -  person Paul Cornelius    schedule 15.03.2016


Ответы (4)


Оператор Python with касается не удаления объектов, а управления ресурсами. Методы __enter__ и __exit__ предназначены для предоставления кода инициализации и уничтожения ресурсов, т. е. вы можете удалить что-то там, но неявное удаление объектов отсутствует. Прочитайте эту with статью, чтобы лучше понять, как ее использовать.

Объект остается в области видимости после оператора with. Вы можете позвонить del, если хотите. Поскольку он находится в области действия, вы можете запросить его после закрытия его базовых ресурсов. Рассмотрим этот псевдокод:

class DatabaseConnection(object):
  def __init__(self, connection):
    self.connection = connection
    self.error = None

  def __enter__(self):
    self.connection.connect()

  def __exit__(self, exc_type, exc_val, exc_tb):
    self.connection.disconnect()

  def execute(self, query):
    try
      self.connection.execute(query)
    except e:
      self.error = e

with DatabaseConnection(connection) as db:
  db.execute('SELECT * FROM DB')
if db.error:
  print(db.error)

del db

Мы бы не хотели, чтобы соединение с базой данных зависало дольше, чем нам нужно (это может понадобиться другому потоку/клиенту), поэтому вместо этого мы разрешаем освобождение ресурса (неявно в конце блока with), но тогда мы можем после этого продолжайте запрашивать объект. Затем я добавил явный del, чтобы сообщить среде выполнения, что код завершен с переменной.

person Alex Taylor    schedule 15.03.2016

with вызывает метод __exit__ объекта при выходе из области действия блока. Ваш метод __exit__ просто печатает object_deleted. Вы должны поместить код для уничтожения объекта внутри вашего метода __exit__ (но обратите внимание, это не очень хорошая практика!).

Что происходит:

with Things('socks','food',12) as stuff:
    # the __enter__() method is called and the returned object
    # is assigned to the variable "stuff"
    greg = stuff.clothes
    print(greg)
# when you've exited the block here, the __exit__() method is called.

Что касается вашего желания явно удалить объект, вы должны оставить это сборщику мусора Python. Прочитайте этот вопрос, это поможет. Вы можете попробовать использовать gc.collect() или переопределить метод __del__. Вот еще одно хорошее обсуждение.

person Chuck    schedule 15.03.2016
comment
Ну, это не на 100% правильно. Вы не можете удалить self внутри __exit__. - person viraptor; 15.03.2016
comment
да, пожалуйста... в ЭТОМ случае покажите мне правильный метод exit для удаления объекта Things - person rikkitikkitumbo; 15.03.2016
comment
Я отредактировал свой ответ, рекомендуя OP оставить его сборщику мусора Python. - person Chuck; 15.03.2016
comment
@ user3583384 Вы не можете этого сделать. Вы выполняете метод объекта, который пытаетесь удалить. Но ваш код очень простой, вам вообще нужно использовать контекстный менеджер? Почему бы и нет: stuff = Things(...); greg = stuff.clothes; print(greg); del stuff ? - person viraptor; 15.03.2016
comment
Это то, что я говорю в своем ответе - with не удаляет. Вам все равно придется вызывать del, если вы хотите попытаться «принудительно» удалить. Вы можете удалить поля объекта внутри __exit__, но объект все еще находится в области действия после блока with. Вы сами это видели на своем примере. - person Alex Taylor; 15.03.2016
comment
@ChuckLoganLim: Вы сказали [...] оставить это сборщику мусора Python. Фактически, в этом случае механизм подсчета ссылок Python удалит объект. Сборщик мусора его даже не увидит. - person Matthias; 15.03.2016
comment
@AlexTaylor: del не удалит объект. del удалит имя, связанное с объектом, и после этого Python может удалить его. - person Matthias; 15.03.2016

Попытка правильно удалить объект Python

Это ваша первая ошибка, интерпретируемые языки явно освобождают вас от необходимости беспокоиться об удалении объектов для освобождения памяти. Что они делают, так это удаляют их, когда больше нет ссылок и объект больше не находится в области видимости.

Во-вторых:

with Things('socks','food',12) as stuff:
    greg = stuff.clothes
    print(greg)

Использование with, несмотря на отступ , не создает новый область действия.

Сравните свой код с:

class Things(object):
   def __init__(self, clothes, food, money):
       self.clothes = clothes
       self.food = food
       self.money = money

   def __enter__(self):
       return self

   def __exit__(self, exc_type, exc_val, exc_tb):
       print('object deleted')

stuff = "no stuff"
def do_it():
    with Things('socks','food',12) as stuff:
        greg = stuff.clothes
        print(greg)
        print(stuff)

do_it()
print(stuff)

Который дает:

socks
<__main__.Things object at 0xb744370c>
object deleted
no stuff

Здесь внутренний stuff внутри do_it() больше не существует, поэтому при запуске второго print(stuff) есть "no stuff".

В конечном счете, не беспокойтесь об удалении объектов. Это произойдет, и не ваша задача управлять этим.

person Community    schedule 15.03.2016

Так что спасибо всем за хорошие ссылки. Но мне очень хотелось на самом деле удалить объект, а не ждать навороченного сборщика мусора. Это сработало:

class Things(object):
   def __init__(self,clothes, food, money):
       self.clothes = clothes
       self.food = food
       self.money = money

   def __enter__(self):
       return self

   def __exit__(self, exc_type, exc_val, exc_tb):
       print('nothing happens')



with Things('socks','food',12) as stuff:
    print(stuff.clothes)
    del stuff


print(stuff)

теперь возвращает:

socks
nothing happens
Traceback (most recent call last):
  File "/home/jeff/PycharmProjects/tensorflow_sandbox/main", line 36, in <module>
    print(stuff)
NameError: name 'stuff' is not defined

Ура! это удалено

person rikkitikkitumbo    schedule 15.03.2016
comment
Вы можете подумать о том, чтобы иметь StuffManager, у которого есть метод __enter__, создающий и возвращающий объект Stuff, и метод __exit__, del создающий его и использующий этот класс в вашем with выражении. - person Paul Evans; 15.03.2016