【问题标题】:Python 'with' not deleting objectPython'with'不删除对象
【发布时间】:2016-06-30 09:16:43
【问题描述】:

尝试正确删除 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

【问题讨论】:

  • 亚历克斯泰勒的答案是正确的。我想补充一点,因为 Python 具有自动内存管理(垃圾收集),您无需担心删除不再使用的对象。因此,您为此目的使用上下文管理器是没有意义的。正如您所观察到的,名为“stuff”的变量也是由with 语句创建的,但在脚本结束之前一直存在。如果您有一行del stuff,则名称“stuff”将变为未定义。您很少需要这样做。

标签: python with-statement


【解决方案1】:

Python 的with 语句不是关于删除对象 - 它是关于资源管理的。 __enter____exit__ 方法用于提供资源初始化和销毁​​代码,即您可以选择在那里删除某些内容,但不会隐式删除对象。阅读此with article 以更好地了解如何使用它。

对象在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 来告诉运行时代码已经完成了变量。

【讨论】:

    【解决方案2】:

    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 的垃圾收集器。阅读此question,它会有所帮助。您可以尝试使用gc.collect() 或覆盖__del__ 方法。这里是another good discussion

    【讨论】:

    • 嗯,这并不是 100% 正确的。您不能删除 __exit__ 中的 self
    • @user3583384 你不能那样做。您正在执行要删除的对象的方法。但是您的代码非常简单,您是否需要使用上下文管理器?为什么不:stuff = Things(...); greg = stuff.clothes; print(greg); del stuff
    • 这就是我在回答中所说的 - with 不会删除。如果您想尝试“强制”删除,您仍然需要致电del。您可以删除__exit__ 内的对象字段,但对象仍在with 块之后的范围内。你已经在你的例子中看到了这一点。
    • @ChuckLoganLim:你说“[...] 把它留给 Python 的垃圾收集器。”。事实上,在这种情况下,Python 的引用计数机制会删除对象。垃圾收集器甚至不会看到它。
    • @AlexTaylor: del 不会删除对象。 del 将删除绑定到对象的名称,然后 Python 可能 将其删除。
    【解决方案3】:

    尝试正确删除 Python 对象

    这是您的第一个错误,解释型语言明确地使您不必担心删除对象以释放内存。他们所做的就是在没有更多引用并且对象不再在范围内时删除它们。

    其次:

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

    使用with,尽管缩进doesn't create a new scope

    对比您的代码:

    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
    

    这里 do_it() 内部的内部 stuff 不再存在,所以当第二个 print(stuff) 运行时有 "no stuff"

    最终,不必担心删除对象。它会发生,而不是你的工作来管理它。

    【讨论】:

      【解决方案4】:

      所以,感谢大家提供的良好链接。但我真的很想真正删除一个对象,而不是等待花哨的垃圾收集器。这有效:

      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
      

      万岁!已删除

      【讨论】:

      • 你可能会考虑有一个StuffManager,它有一个__enter__方法创建和返回一个Stuff对象和一个__exit__方法deling它并在你身上使用那个类@987654328 @ 声明。
      • @Paul Evans 在这种情况下这不是解决方案,因为仍然存在对范围外对象的引用,而 del 仅删除变量(在这种情况下为 self)并减少引用从二数到一
      【解决方案5】:

      在上下文管理器中,您可以通过返回对 Things 实例的弱引用来实现,这不会增加对象的引用计数,因此一旦我们 __exit__ 和不再使用对象的最后一个引用。 这样退出上下文管理器后就不用手动调用delete了。

      一般来说,这可以简单地通过包装__enter__函数的返回值来完成:

      import weakref
      
      class Things(object):
         ...
      
         def __enter__(self):
             return weakref.proxy(self)
      

      您的整个代码片段如下所示:

      import weakref
      class Things(object):
         def __init__(self, clothes, food, money):
             self.clothes = clothes
             self.food = food
             self.money = money
      
         def __enter__(self):
             return weakref.proxy(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)
      
      
      try:
          print(stuff.clothes)
      except ReferenceError as e:
          print('Could not access stuff', e)
      
      try:
          print(stuff)
      except ReferenceError as e:
          print('Could not access stuff', e)
      
      print(stuff is None, type(stuff))
      

      程序的输出是

      socks
      object deleted
      Could not access stuff weakly-referenced object no longer exists
      Could not access stuff weakly-referenced object no longer exists
      False <class 'weakproxy'>
      

      正如您在程序输出中看到的那样,此解决方案的唯一警告是,尝试访问变量 stuff 时抛出的异常与删除对象时抛出的异常不同,并且 @ 987654329@ 属于弱代理类型。但出于所有意图和目的,这个解决方案做了它应该做的事情:当我们尝试访问一个不应再访问的对象时引发异常。

      此解决方案适用于 python2.7 和 python3.x。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-09-02
        • 1970-01-01
        • 1970-01-01
        • 2019-05-12
        • 1970-01-01
        • 1970-01-01
        • 2018-04-20
        • 1970-01-01
        相关资源
        最近更新 更多