我有一个大型脚本,我发现很多与机器的连接都是打开的,原因是其中一个类析构函数从未被调用过。
下面的是显示该问题的简化脚本版本。 我分层搜索并发现它可能是因为GC和weakref确实有帮助,但在这种情况下没有帮助。
我可以看到析构函数被调用的2个案例是
如果我调用B_class对象而不传递A_class函数
self.b = B_class("AA")
我称make的B_class对象不是全局的,即不使用self
b = B_class("AA",self.myprint)
b.do_something()
这两种情况都会给我的案子带来进一步的问题。最后的办法是自己关闭/删除对象,但我不想这样做。
任何人都可以建议更好的方法来帮助我理解这个问题吗?提前谢谢。
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()
答案 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
在您的情况下,您将需要使用其他名称(连接或其他内容)而不是“打开”,并在上下文管理器内部执行套接字连接/断开连接。