Zuerst habe ich eine Aufnahmeklasse mit einer Flush-Methode geschrieben:

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)

Dann wollte ich am Ausgang spülen: wenn manuell gestoppt, abgestürzt oder aus welchem Grund auch immer.

Also habe ich verwendet:

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

Und es hat ziemlich gut funktioniert.

Aber jetzt möchte ich mehrmals mit einer anderen Puffergröße und auf einem anderen Pfad aufnehmen, die Aufnahme stoppen, erneut aufnehmen. Also muss ich mehrere Recorder verwerfen und dann instanziieren. Es funktioniert irgendwie, aber der Speicher älterer Recorder (der etwas Fett self._big_buffer̀ enthält) wird nicht freigegeben, da er von atexit beibehalten wird. Auch wenn ich explizit del aufrufe. Ich kann nicht atexit.unregister(self._flush), da es nur Python 3 ist.

Ich würde es vorziehen, vorhandene Instanzen nicht wiederzuverwenden, sondern ältere Instanzen zu verwerfen und neue zu erstellen.

Wie würden Sie mit einem solchen Fall umgehen?

2
Cyrille 19 Aug. 2015 im 17:13

4 Antworten

Beste Antwort

Sie können versuchen, einen schwachen Verweis auf den atexit -Handler zu verwenden, also auf das Objekt wird nicht beibehalten, wenn es an anderer Stelle gelöscht wird:

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'

Die Methode wird als Zeichenfolge übergeben, um viele Probleme mit zu vermeiden gebundene Methode schwache Referenzen, wenn Sie es störend finden, können Sie immer verwenden Eine BoundMethodWeakref Implementierung wie diese: http: //code.activestate .com / rezepte / 578298-gebundene-methode-schwachref /

4
mguijarr 19 Aug. 2015 im 15:20

Sicherlich lautet die Antwort , damit Ihr Recorder Pfade und Puffereigenschaften nach Belieben ändern kann. Sie sagen "Ich würde es vorziehen, vorhandene Instanzen nicht wiederzuverwenden, sondern ältere Instanzen zu verwerfen und neue zu erstellen." Sie geben jedoch keine Begründung dafür an. außer vielleicht Ihrer Annahme, dass der Speicher des "älteren Rekorders (der etwas Fett self._big_buffer̀ enthält) nicht freigegeben wird, da er von atexit" beibehalten wird, was meiner Meinung nach falsch ist.

Zwar behält atexit einen Verweis auf das Rekorderobjekt bei, dies bedeutet jedoch nur, dass der Pufferspeicher erhalten bleibt, solange der Rekorder darauf verweist. Es wäre ziemlich einfach, eine close() -Methode wie z

    def close(self):
        self.flush()
        self._big_buffer = None

Und Bingo! Es gibt keinen Verweis auf den Pufferspeicher und er kann gesammelt werden.

Ihre __init__() -Methode sollte sich einfach bei atexit registrieren. Anschließend kann die open() -Methode (die den Rest der aktuellen __init__() -Methode ausführt) mehrmals verwendet werden, gefolgt von jeder ein close() Aufruf.

Zusammenfassend denke ich, dass Ihr Problem nach einem einzelnen Objekt schreit.

0
holdenweb 19 Aug. 2015 im 18:46

Sie können das Handle von Hand aus der Liste (undokumentiert) atexit._exithandlers entfernen.

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

Ich hoffe, das hilft.

0
VincenzoC 28 Dez. 2018 im 11:32

Ich würde sagen, Sie versuchen, das falsche Werkzeug zu verwenden. Die with -Anweisung und die Kontextmanager sind hierfür ein sehr gutes Werkzeug. Datei-E / A ist das Hauptbeispiel, mit dem die meisten Python-Benutzer die with-Anweisung kennenlernen.

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

Wird:

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

Sie können Ihre eigenen Kontextmanager erstellen, indem Sie die Methoden __enter__ und __exit__ für Ihre Klasse schreiben.

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() ?

Sie würden dann Ihr Recorder Objekt wie folgt verwenden:

with Recorder(...) as recorder:
    # operations with recorder
    ...
# regardless of what happens the recorder will be flushed at this point
2
Dunes 19 Aug. 2015 im 18:40