removeHandler in __del__-Methode einer Klasse. Clever?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
kniepbert
User
Beiträge: 3
Registriert: Dienstag 12. Mai 2009, 19:00
Wohnort: Tübingen

Hallo Forum,

ich bin seit einigen Wochen python-Fan; mit wachsender Begeisterung sogar. :)

Ich möchte nun eine Klasse erschaffen, die bei der Erstellung den logger derart abändert, dass in ein seperates File mitgeloggt wird.
Wird die Klasse wieder gelöscht, dann soll der zusätzliche Handler wieder gelöscht werden und das "Zusatzlogfile" wird ins Archiv geschoben.
Ich bewege mich noch vorsichtig in den Klassen... dachte jedoch, dass das ganz nett ist.
Ich könnte logger sicher auch global definieren, das wollte ich jedoch vermeiden; ist doch irgendwie unschön, falls ich mal parallel einen Thread im Skript öffne... FWIW

Auf jeden Fall wird der extra Handler angehängt, aber leider nicht wieder gelöscht, wenn zum Schluss ein "del media" gemacht wird... :(

Mein Create-logger sieht so aus:

Code: Alles auswählen

def create_logger(LOG_FILENAME):
   # create logger
   logger = logging.getLogger()
   logger.setLevel(logging.DEBUG)
 
   handler = logging.handlers.RotatingFileHandler(
             LOG_FILENAME, maxBytes=500000, backupCount=5)
   handler.setLevel(logging.INFO)
   # create formatter
   formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
   # add formatter
   handler.setFormatter(formatter)
   # add handler to logger
   logger.addHandler(handler)
   return logger
Meine Klasse habe ich so erdacht.

Code: Alles auswählen

class media(object):
   def __init__(self, CURSOR, logger, options, ID):
      QUERY =  "SELECT * FROM medias WHERE id='%s'" % (str(ID))
      if options.debug:
         logger.debug(QUERY)
      CURSOR.execute(QUERY)
      MEDIA = CURSOR.fetchone ()
      self.id  = MEDIA[0]
      self.files = []
      self.logfile = "%s_%s_%s.log" % (self.id, time.time(), vbc.RandomString(5));
      
      # Logger
      hdlr = logging.handlers.RotatingFileHandler("/var/log/%s" % self.logfile, maxBytes=500000, backupCount=5)
      hdlr.setLevel(logging.DEBUG)
      formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
      hdlr.setFormatter(formatter)
      logger.addHandler(hdlr)
      self.logger = logger
    
   def __del__(self):
      # Del bestaetigen
      self.logger.info("Objekt der id '%s' wurde geloescht" % self.id)
      # Handler wegschmeissen
      hdlr = logging.handlers.RotatingFileHandler("/var/log/%s" % self.logfile, maxBytes=500000, backupCount=5)
      hdlr.setLevel(logging.DEBUG)
      formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
      hdlr.setFormatter(formatter)
      
      hdlr.flush()  
      hdlr.close()  
      self.logger.removeHandler(hdlr)  
      
      # Logfiles in "old" verschieben....
      commands.getoutput("mv /var/log/%s /var/log/old/%s"% (self.logfile, self.logfile))
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

http://docs.python.org/reference/datamo ... ject.__del__

__del__ wird nicht empfohlen (such mal im Forum danach, gibt genug Threads dazu).
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hallo und willkommen im Forum!

Für einen Newbie liest sich der Code gut, sehr gut sogar (solltest aber vielleicht mal den style guide durchlesen). Und ich finde die Idee __del__ zu verwenden gar nicht soo schlecht hier: Ist schließlich dazu gedacht das aufzuräumen, was Python selber nicht kann - und genau eine der wenigen Ausnahmen ist hier erfüllt, oder sehe ich das falsch?

Aber vielleicht solltest Du die Nutzer der Klasse auffordern den Handler selber zu schließen (ermöglicht auch die entsprechende Fehlerbehandlung, falls erforderlich) und die entsprechende Funktion in __del__ aufrufen, falls der Handler noch existiert und der expliziete Aufruf vergessen wurde. Das ist zwar kein 100%iger Schutz, aber besser als nichts.

Ach, und Code kannst Du manchmal besser in ein pastbin schieben, wenn es mal etwas mehr wird: Auf diese Weise wird das Forum entlastet und das Antworten auf Threads fällt leichter. (Jedenfalls hatte ich gerade Probleme, obwohl Du eigentlich gar nicht so viel Code gepostest hast. *kopfkratz*)

Gruß,
Christian
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Das ist eben die Frage, ob ``__del__`` wirklich zum Aufräumen gut ist oder ob man mit etwas Anderem wie beispielsweise dem `with`-Statement nicht besser beraten wäre. Zum Einen ist ``__del__`` nicht dieser deterministische Destruktor, wie man ihn aus anderen Sprachen kennt. Es wird nicht garantiert, dass ``__del__`` überhaupt aufgerufen wird. Und zum anderen kann es eben passieren, dass *gerade* durch ``__del__`` das Objekt niemals aufgeräumt werden kann. So werden beispielsweise zirkuläre Referenzen, bei denen ein ``__del__`` involviert ist, niemals aufgelöst (Beispiel hier).
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
kniepbert
User
Beiträge: 3
Registriert: Dienstag 12. Mai 2009, 19:00
Wohnort: Tübingen

Nabert Ihr,

@Christian
Vielen Dank für die Blumen... Ich habe vorher schon ein paar Jahre Bash-Programmiert, eigentlich immer recht hässlich. Auf meine alten Tage studiere ich gerade noch Informatik und mein Code / meine Codedisziplin hat sich schon erheblich gebessert.
Die Frage die sich für mich in der __del__-Funktion stellt ist, wie ich den logger wieder aus dieser herausbekomme. Ein return-Wert hat sie nicht, wenn ich das richtig sehe. Daher bekommt das Programm dann schwer mit, dass sich der Logger geändert hat. Sehe ich doch richtig, oder? Wenn ich ein logger.removeHandler mache und der logger in der Funktion die "del bla" aufruft nicht überschrieben wird, dann ist Essig mit dem remove.

@Trundle
Oha, die Formulierung kommt mir irgendwie aus meiner Info-Vorlesung bekannt vor. :)
Du schreibst von einem With-Statement....
Der allwissende Mitarbeit er bei Google schickt mich zur Python-Doku.
Habe ich noch nicht ganz durchgeistigt, muss ich mal ne Nacht drüber schlafen.

Zu pastbin:
Nur blöd, dass nach einem Monat der Code wech ist. Dann schauen Suchende in die Röhre und das Forum ist ein inkosistentes Datengrab... hehe... :)
[/url]
BlackJack

@kniepbert: Das der Quelltext bei paste.pocoo.org nach einem Monat gelöscht wird, wäre mir neu. Das ist in Python geschrieben und die Autoren sind hier im Forum keine Unbekannten.
kniepbert
User
Beiträge: 3
Registriert: Dienstag 12. Mai 2009, 19:00
Wohnort: Tübingen

Asche auf mein Haupt... der Keks wird nach 31 Tagen gelöscht....
Ist ja auch schon spät. :)
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Trundle hat geschrieben:Das ist eben die Frage, ob ``__del__`` wirklich zum Aufräumen gut ist oder ob man mit etwas Anderem wie beispielsweise dem `with`-Statement nicht besser beraten wäre. Zum Einen ist ``__del__`` nicht dieser deterministische Destruktor, wie man ihn aus anderen Sprachen kennt. Es wird nicht garantiert, dass ``__del__`` überhaupt aufgerufen wird. Und zum anderen kann es eben passieren, dass *gerade* durch ``__del__`` das Objekt niemals aufgeräumt werden kann. So werden beispielsweise zirkuläre Referenzen, bei denen ein ``__del__`` involviert ist, niemals aufgelöst (Beispiel hier).
Wir haben hier aber nicht das Problem zirkulärer Referenzen, sondern das Problem einen Logger zu schliessen und zu "backupen". Mein erster Rat war auch diese Funktion explizit zu definieren und den Nutzer anzuhalten diese Funktion explizit aufzurufen. Aber was, wenn dieser den entsprechenden Code nicht schreibt? Dann ist das with-statement eine gute Idee! Aber dessen Verwendung liegt ebenfalls auf der Nutzerseite (oder?)*. In dem Fall ist ein Aufruf der "Logger-Aufräumfunktion" in __del__ eine Idee - unter den genannten Einschränkungen, da hast Du, Trundle, absolut recht! Es ist damit nicht garantiert, dass das Aufräumen zum gewünschten Zeitraum geschieht, erhöht aber die Sicherheit ein *wenig*.

Gruß,
Christian

*All dies natürlich irrelevant, wenn Nutzer der Klasse == primärer Programmierer. Dann kann man sich den Schnickschnack besser sparen.
BlackJack

@CM: Es erhöht die Sicherheit nicht wirklich, IMHO verringert sie sich sogar. Wenn der Programmierer nicht sauber arbeitet, dann hat er halt Pech gehabt. Das kann man nicht erzwingen.

Mit dem `__del__()` fördert man die schlampige Programmierung eher noch, weil das in der Regel *halbwegs* funktioniert. Immer wieder gerne gehörte Begründung warum Leute auf das explizite `close()` bei Dateiobjekten verzichten.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Da __del__() so viele Probleme birgt, wieso ist es nicht längst aus Python verschwunden?
Antworten