python析构函数没有被调用

时间:2015-09-26 01:18:38

标签: python destructor

我有一个大型脚本,我发现很多与机器的连接都是打开的,原因是其中一个类析构函数从未被调用过。

下面的

是显示该问题的简化脚本版本。 我分层搜索并发现它可能是因为GC和weakref确实有帮助,但在这种情况下没有帮助。

我可以看到析构函数被调用的2个案例是

  1. 如果我调用B_class对象而不传递A_class函数

    self.b = B_class("AA")
    
  2. 我称make的B_class对象不是全局的,即不使用self

    b = B_class("AA",self.myprint)
    b.do_something()
    
  3. 这两种情况都会给我的案子带来进一步的问题。最后的办法是自己关闭/删除对象,但我不想这样做。

    任何人都可以建议更好的方法来帮助我理解这个问题吗?提前谢谢。

    import weakref
    
    
    class A_class:
    
        def __init__(self,debug_level=1,version=None):
            self.b = B_class("AA",self.myprint)
            self.b.do_something()
    
        def myprint(self, text):
            print text
    
    class B_class:
    
        def __init__(self,ip,printfunc=None):
            self.ip=ip
            self.new_ip =ip
            #self.printfunc = printfunc
            self.printfunc = weakref.ref(printfunc)()
        def __del__(self):
            print("##B_Class Destructor called##")
    
        def do_something(self,timeout=120):
            self.myprint("B_Class ip=%s!!!" % self.new_ip)
    
        def myprint(self,text):
            if self.printfunc:
                print ("ExtenalFUNC:%s" %text)
            else:
                print ("JustPrint:%s" %text)
    
    def _main():
      a = A_class()
    
    if __name__ == '__main__':
        _main()
    

3 个答案:

答案 0 :(得分:0)

self.printfunc = weakref.ref(printfunc)()

实际上并没有使用weakref来解决您的问题;这条线实际上是一个noop。您使用weakref创建weakref.ref(printfunc),但是您可以使用调用parens进行跟踪,调用parens会从weakref转换回您存储的强引用(以及weakref对象消失)。显然,不可能将weakref存储到绑定方法本身(因为绑定方法是每次在self上引用时创建的自己的对象,而不是其生存期与{{1}相关联的缓存对象所以你必须得到一些hacky,取消绑定方法,这样你就可以对对象本身采用self。}}) Python 3.4引入了WeakMethod来简化这一过程,但是如果你不能使用它,那么你就会被卡住。

尝试将其更改为(在Python 2.7上,您必须weakref):

import inspect

并将# Must special case printfunc=None, since None is not weakref-able if printfunc is None: # Nothing provided self.printobjref = self.printfuncref = None elif inspect.ismethod(printfunc) and printfunc.im_self is not None: # Handling bound method self.printobjref = weakref.ref(printfunc.im_self) self.printfuncref = weakref.ref(printfunc.im_func) else: self.printobjref = None self.printfuncref = weakref.ref(printfunc) 更改为:

myprint
是的,这很难看。如果你愿意,你可以把丑陋分解出来(借用the implementation of WeakMethod from Python 3.4's source code会有意义,但是名字必须改变; def myprint(self,text): if self.printfuncref is not None: printfunc = self.printfuncref() if printfunc is None: self.printfuncref = self.printobjref = None # Ref died, so clear it to avoid rechecking later elif self.printobjref is not None: # Bound method not known to have disappeared printobj = self.printobjref() if printobj is not None: print ("ExtenalFUNC:%s" %text) # To call it instead of just saying you have it, do printfunc(printobj, text) return self.printobjref = self.printfuncref = None # Ref died, so clear it to avoid rechecking later else: print ("ExtenalFUNC:%s" %text) # To call it instead of just saying you have it, do printfunc(text) return print ("JustPrint:%s" %text) 在Py2中是__self__im_self是{{1}虽然如此,但它仍然令人不快。如果__func__实际上可能变暗,那绝对不是线程安全的,因为im_func成员的检查和清除不受保护。

答案 1 :(得分:0)

您没有正确使用weakref.ref对象。你在创建后立即调用它,它返回引用的对象(传递给printref的函数)。

通常情况下,您需要保存弱引用,并且只在您要使用reffered-to对象时调用它(例如在myprint中)。但是,这对于作为self.myprint传入的绑定方法printfunc不起作用,因为绑定的方法对象没有任何其他引用(对方法的每次访问都会创建一个新的对象)。

如果您使用的是Python 3.4或更高版本,并且您知道传入的对象将始终是绑定方法,则可以使用WeakMethod class,而不是常规ref。如果你不确定你将获得什么类型的可调用,你可能需要进行一些类型检查,看看是否需要WeakMethod

答案 2 :(得分:0)

使用Python的“with”语句(http://www.python.org/dev/peps/pep-0343/)。 它创建了一个语法范围,它创建的 __ exit __ 函数保证在执行离开范围后立即调用。您还可以通过使用contextlib模块中的“contextmanager”装饰器创建一个生成器来模拟“__enter __ / __ exit__”行为(python 2.6+或2.5使用“from __future__ import with_statement”,参见PEP示例)。

以下是PEP的一个例子:

import contextlib

@contextlib.contextmanger
def opening(filename):
   f = open(filename) # IOError is untouched by GeneratorContext
   try:
       yield f
   finally:
       f.close() # Ditto for errors here (however unlikely)

然后在主代码中编写

with opening(blahblahblah) as f:
    pass
    # use f for something
# here you exited the with scope and f.close() got called

在您的情况下,您将需要使用其他名称(连接或其他内容)而不是“打开”,并在上下文管理器内部执行套接字连接/断开连接。