无法从 python 2.7 中的 atexit 注销函数

Cannot unregister functions from atexit in python 2.7

首先我用flush方法写了一个记录class:

class Recorder
    def __init__(self, buffer_size, path):
        self._big_buffer = np.array(*buffer_size)
        self._path = path
    def push(self, data):
        # insert in self._big_buffer
        # if self._big_buffer is full:
        #     self._flush()
    def flush(self):
        # write buffer to disk (self._path)

然后,我想在退出时刷新:当手动停止、崩溃或任何原因时。

所以我用了:

def __init__(self):
    (...)
    atexit.register(self.flush)

而且效果很好。

但是现在,我想记录,停止记录,再次记录,多次,不同的缓冲区大小和不同的路径。所以我必须丢弃,然后实例化几个Recorder。它有点工作,但是旧 Recorder 的内存(包含一些 fat self._big_buffer̀)没有被释放,因为它被 atexit 保留。即使我明确调用 del。 我不能 atexit.unregister(self._flush) 因为它只有 Python 3。

我不希望重用现有实例,而是丢弃旧实例并创建新实例。

遇到这种情况你会怎么处理?

您可以尝试使用对 atexit 处理程序的弱引用,这样对象 如果在其他地方被删除,将不会被保留:

import atexit
import weakref

class CallableMethodWeakRef:
    def __init__(self, object, method_name):
        self.object_ref = weakref.ref(object)
        self.method_name = method_name
    def __call__(self):
        object = self.object_ref()
        if object:
            getattr(object, self.method_name)()

class Recorder:
    def __init__(self, *args):
        atexit.register(CallableMethodWeakRef(self, 'flush'))

    def flush(self):
        print 'flushing'

该方法作为字符串传递以避免很多问题 绑定方法弱引用,如果你觉得它令人不安,你可以随时使用 像这样的 BoundMethodWeakref 实现:http://code.activestate.com/recipes/578298-bound-method-weakref/

答案 肯定是 允许您的 Recorder 随意更改路径和缓冲区特性。你说 "I would prefer not to reuse existing instances, but discarding older instances and create new ones." 但你没有给出任何理由, 也许除了你假设 "older Recorder's memory (containing some fat self._big_buffer̀) is not freed since it's retained by atexit",我认为这是不正确的。

虽然 atexit 确实保留了对记录器对象的引用,但这仅意味着只要记录器引用它,就会保留缓冲区内存。添加 close() 方法如

会很容易
    def close(self):
        self.flush()
        self._big_buffer = None

还有宾果游戏!不存在对缓冲存储器的引用,并且它是可收集的。

你的 __init__() 方法应该简单地注册到 atexit,然后 open() 方法(它完成 __init__() 当前所做的其余部分)可以多次使用, 每个后跟一个 close() 调用。

总而言之,我认为您的问题需要一个对象。

我会说您尝试使用错误的工具。 with 语句和上下文管理器是一个很好的工具。文件 IO 是大多数 python 用户将了解 with 语句的主要示例。

f = open("somefile.txt", "w")
try:
    f.write("...")
    # more file operations
finally:
    # regardless of what happens, make sure the files is closed 
    f.close()

变为:

with open("somefile.txt", "w") as f:
    f.write("...")
    # more file operations
# close automatically called at the end of the block

您可以通过为 class.

编写 __enter____exit__ 方法来创建自己的上下文管理器
class Recorder
    def __init__(self, buffer_size, path):
        self._big_buffer = np.array(*buffer_size)
        self._path = path
    def push(self, data):
        # insert in self._big_buffer
        # if self._big_buffer is full:
        #     self._flush()
    def flush(self):
        # write buffer to disk (self._path)
    def __enter__(self):
        return self
    def __exit__(self, exctype, exception, traceback):
        # If an exception was thrown in the with block you will get the details here. 
        # If you want the say that the exception has been handled and for it not to be 
        # raised outside the with block then return True
        self.flush()
        # self.close() ?

然后您可以像这样使用 Recorder 对象:

with Recorder(...) as recorder:
    # operations with recorder
    ...
# regardless of what happens the recorder will be flushed at this point

您可以从(未记录的)atexit._exithandlers 列表中手动删除句柄。

import atexit

def unregister(func, *targs, **kargs):

    """unregister a function previously registered with atexit.
       use exactly the same aguments used for before register.
    """
    for i in range(0,len(atexit._exithandlers)):
        if (func, targs, kargs) == atexit._exithandlers[i] :
            del atexit._exithandlers[i]
            return True
    return False

希望对您有所帮助。