Seite 1 von 1

Von file geerbt

Verfasst: Donnerstag 23. Juli 2009, 08:26
von HarryH
Hallo,

Habe mir eine kleine Klasse geschrieben die von file erbt. In dieser Klasse habe ich eine Progress-Funktionalität eingebaut, z.B. zur Realisierung von Fortschrittsanzeigen.
An mein file-object kann nun eine progress Funktion übergeben werden, die ein Argument empfängt, nämlich die geschriebene Buffer-Länge. Innerhalb dieser Funktion kann dann z.B. eine Fortschrittsanzeige angetrieben werden.

Nun würde ich gerne eure Meinung dazu wissen. Ist das korrekt so wie ich das mache?

Hier nun die Klasse:

Code: Alles auswählen

class MyFile(file):
    """Special file object, with progress functionality"""
    stdwrite = file.write
    stdread = file.read

    def __init__(self, filename, mode='r', bufsize=-1, progress_func=None):
        file.__init__(self, filename, mode, bufsize)
        self.progress_func = progress_func
        
    def write(self, s):
        self.stdwrite(s)
        # call progress function
        buflen = len(s)
        self.progress_func and buflen and self.progress_func(value=buflen)

    def read(self, size=-1):
        s = self.stdread(size)
        # call progress function
        buflen = len(s)
        self.progress_func and buflen and self.progress_func(value=buflen)
        return s

Verfasst: Donnerstag 23. Juli 2009, 09:09
von veers
Das sieht für mich Gefährlich aus. Wenn file.writelines verwendet wird tut das zum Beispiel nicht. Wenn du das anders lösen kannst dann würde ich das anders lösen. ;)

Gruss,
Jonas

Verfasst: Donnerstag 23. Juli 2009, 09:26
von snafu
Mal von dem Sinn der ganzen Klasse abgesehen, frage ich mich, warum `progress_func` ein Keyword-Argument ist. Die sind ja eher dafür gedacht, dass man Vorgaben übernimmt, wenn man nichts anderes will. Ich wüsste aber nicht, wie die Klasse funktionieren soll, wenn sie `None` erhält...

Verfasst: Donnerstag 23. Juli 2009, 09:27
von BlackJack
@HarryH: Der Name der Klasse ist etwas unspezifisch.

Die bedingte Ausführung mit ``and`` statt mit einem ordentlichen ``if`` ist in Python unüblich.

Ich würde auch die "Sonderfälle" eliminieren, oder zumindest dokumentieren warum die Default-Rückruffunktion `None` ist und warum `stdwrite` und `stdread` auf Klassenebene definiert werden. Ich würd's erst einmal anders machen bis ich wirklich gemessen habe, dass das zu langsam ist. Kann natürlich sein, dass Du das schon getan hast.

Du zwingst den Benutzer der Klasse das Argument der Rückruffunktion `value` zu nennen. Warum?

Code: Alles auswählen

class MyFile(file):
    """Special file object, with progress functionality"""

    def __init__(self,
                 filename,
                 mode='r',
                 bufsize=-1,
                 progress_func=lambda data_size: None):
        file.__init__(self, filename, mode, bufsize)
        self.progress_func = progress_func
    
    def write(self, data):
        if data:
            file.write(self, data)
            self.progress_func(len(data))

    def read(self, size=-1):
        result = file.read(self, size)
        self.progress_func(len(result))
        return result
Letztlich sollte man sich aber gut überlegen, ob man von `file` erben möchte und nicht lieber eine Proxy-Klasse schreibt, denn beim Erben von `file` muss man auf jeden Fall auch noch dokumentieren was alles nicht funktioniert! `file.readline()` oder das Iterieren über die Datei gehen zum Beispiel an diesen beiden Methoden vorbei. Und man wäre mit einem Proxy auch flexibler, weil man zum Beispiel dort auch andere "file like"-Objekte reinstecken kann, beispielsweise das was `urllib.urlopen()` zurück gibt.

Verfasst: Donnerstag 23. Juli 2009, 09:33
von snafu
Ich würde wie gesagt eher zu garkeinem Keyword-Argument tendieren, als zu einem Lambda-Konstrukt, welches das Argument verschluckt. Oder übersehe ich hier irgendwelche Vorteile?

Verfasst: Donnerstag 23. Juli 2009, 10:07
von BlackJack
@snafu: Ich verstehe gerade nicht was Du mit Keyword-Argument meinst!? Und wie würdest Du das anders lösen?

Verfasst: Donnerstag 23. Juli 2009, 10:50
von snafu

Code: Alles auswählen

def __init__(self, filename, progress_func, mode='r', bufsize=-1)
oder, wenn man den vorherigen Konstruktor behalten will:

Code: Alles auswählen

if self.progress_func:
    self.progress_func(len(data))
Ich verstehe halt nicht, was der Umweg über Lambda bringen soll.

Verfasst: Donnerstag 23. Juli 2009, 11:16
von BlackJack
@snafu: Der eleminiert den Sonderfall, den Du mit dem ``if`` prüfst und dient gleichzeitig als Dokumentation. Man weiss dann das da eine Funktion erwartet wird, die ein Argument entgegennimmt und wenn das halbwegs passend benannt ist, ist das auch nochmal "Doku".

Ich mags halt nicht, wenn ich an allen Ecken und Enden prüfen muss, ob etwas `None` ist, wenn ich an *einer* Stelle dafür sorgen kann, dass es mit einem Wert initialisiert wird, der einfach so verwendet werden kann.

Verfasst: Donnerstag 23. Juli 2009, 12:15
von HarryH
Hallo an alle,

Vielen Dank für eure nützlichen Anregungen!

@BlackJack: Das mit der lamba funktion ist ein guter Tip. Ich spare mir dann die Prüfung innerhalb der Methoden und es dient der Doku.

Das mit dem Argument 'value' hat sich so aus meiner individuellen Anwendung der Funktion ergeben. Habe es schon geändert; ist wirklich unnötig.
Die Geschwindigkeit habe ich allerdings noch nicht gemessen.

Bseonders interessant fand ich

Code: Alles auswählen

file.write(self, data)
file.read(self, size)
anstelle meiner

Code: Alles auswählen

stdwrite = file.write
stdread = file.read
Das hatte mich nämlich am meisten gestört. Ich wußte nur nicht das es auch besser geht.