Zip-Datei nur im Speicher erzeugen

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
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Hallo,

Mein Programm verarbeitet mehrere Dateien, die ich vor der Weiterverarbeitung gerne zippen möchte. Da die Zip-Datei allerdings nur ein Zwischenschritt ist und nachher nicht mehr gebraucht wird, würde ich die gerne nur im Speicher erzeugen.

Geht das wohl irgendwie? Wenn ich mir https://docs.python.org/3.4/library/zipfile.html anschaue, dann steht dort nur, dass man eine Datei öffnen muss, aber das würde ich ungern machen. Temporäre Dateien irgendwo zu erzeugen, nachher wieder zu löschen usw. scheint mir unsinnig zu sein, wenn ich die Datei auch im Speicher lassen könnte. Das wird sich eh nur um einige kleinere Dateien handeln, so dass die komplett im Speicher sein können und nicht zu groß sind.

Danke!
BlackJack

@Hellstorm: Wo steht da das man eine Datei öffnen muss? `ZipFile` nimmt als erstes Argument `file` einen Pfad oder ein dateiähnliches Objekt („where file can be either a path to a file (a string) or a file-like object”). Das wäre zum Beispiel ein `io.BytesIO`-Objekt.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Sehr schön, das klappt ja super.

Wieder einmal vielen Dank an dich für deine Hilfe! Auch wenn ich immer recht offensichtliche Fragen stelle, fügt sich so langsam das Gesamtbild zusammen :)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Hellstorm: Na, so offensichtlich ist die Antwort nun auch wieder nicht. Zumindest wenn man noch nicht soviel Erfahrung mit Python hat, dann weiß man nicht zwangsläufig, dass man mit Python auch Dateiobjekte erstellen kann, die keiner auf einem Datenträger liegenden Datei zugeordnet sind und dass andere Python-Funktionen diese dann problemlos weiterverarbeiten können. Sowas "darf" man IMHO auch als Anfänger mit Grundkenntnissen ruhig fragen. ;)
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Ich hab jetzt mal mit dem Ipython Notebook mit io.BytesIO und zipfile.ZipFile herumgespielt und jetzt habe ich das glaube ich verstanden. :D

Das Ipython Notebook ist ja echt cool, wenn man solche neuen Konzepte ausprobieren will, da man ja alles schön selber kommentieren kann und das nachher noch einmal anschauen kann :)
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Hab mir jetzt mal eine eigene Klasse erstellt, die eine virtuelle Zip-Datei macht:

Code: Alles auswählen

class VirtualFile(io.BytesIO):
    def __init__(self, list_of_files):
        self._create_zipfile(list_of_files)

    def _create_zipfile(self, files):
        with zipfile.ZipFile(self, "w") as zip_file:
            for file in files:
                zip_file.write(file)

    def printdir(self):
        with zipfile.ZipFile(self, "r") as zip_file:
            zip_file.printdir()
Also klappt auf jeden Fall schon mal problemlos :D Jetzt muss man einfach nur test = VirtualFile("test.txt", "bla.txt") machen und anschließend ist test eine Zipdatei.

Könnte ich das vom Prinzip noch verbessern?
BlackJack

@Hellstorm: Ich sehe nicht so ganz, dass das eine Klasse ist. Und `printdir()` funktioniert ohne das man den Dateizeiger wieder an den Anfang setzt?
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

BlackJack hat geschrieben:@Hellstorm: Ich sehe nicht so ganz, dass das eine Klasse ist. Und `printdir()` funktioniert ohne das man den Dateizeiger wieder an den Anfang setzt?
Wie, das ist keine Klasse? Was meinst du damit?

printdir() scheint aber zu funktionieren, also ich wüsste nicht wieso nicht :K

Code: Alles auswählen

In [6]: class VirtualFile(io.BytesIO):
   ...:         def __init__(self, list_of_files):
   ...:                 self._create_zipfile(list_of_files)
   ...:         def _create_zipfile(self, files):
   ...:                     with zipfile.ZipFile(self, "w") as zip_file:
   ...:                             for file in files:
   ...:                                     zip_file.write(file)
   ...:         def printdir(self):
   ...:                     with zipfile.ZipFile(self, "r") as zip_file:
   ...:                             zip_file.printdir()
   ...:                 

In [7]: datei = VirtualFile(["hallo.txt", "blabla.txt"])

In [8]: datei.printdir()
File Name                                             Modified             Size
hallo.txt                                      2014-04-07 23:13:38            6
blabla.txt                                     2014-04-07 23:13:42            7

In [9]: datei.printdir()
File Name                                             Modified             Size
hallo.txt                                      2014-04-07 23:13:38            6
blabla.txt                                     2014-04-07 23:13:42            7
BlackJack

@Hellstorm: Syntaktisch ist es natürlich eine Klasse, aber nicht alles was man in eine Klasse steckt ist auch tatsächlich eine Klasse, also gehört da auch rein. Hier sehe ich keinen wirklichen Vorteil gegenüber zwei einfachen Funktionen. Der Name ist auf jedenfall nicht gut, denn ein `VirtualFile` ist `BytesIO` ja schon.

Edit: Die Vererbung macht auch keinen Sinn. Das resultat hat eine Menge Methoden von `BytesIO` geerbt, die man gar nicht verwenden darf oder gar nicht sinnvoll verwenden kann. Zumindest muss man dabei aufpassen den internen Zustand — eine ZIP-Datei, dabei nicht kaputt zu machen.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Hellstorm: Klassen sollten Tätigkeiten vereinen, die sinnvollerweise zu ihnen passen. Es ist jedoch nicht gerade sinnvoll, dass ein `VirtualFile`-Objekt - also eine Datei - einen Verzeichnisinhalt ausgeben kann. Außerdem nutzen Klassenmethoden normalerweise gemeinsame Instanzattribute (`self.myname`), da man durch Verwendung einer Klasse vermeiden möchte, dass diese Attribute andernfalls als Funktionsargumente herumgereicht werden müssten. Auch diesen Anwendungsfall sehe ich bei dir nicht. Du nutzt hier also eine Klasse, die eine sehr fragwürdige Benutzerschnittstelle anbietet. Man kann sowas natürlich immer machen, aber gerade logisch erscheint es in dem Fall nicht.

EDIT: Ich habe jetzt nicht auf Anhieb geschnallt, dass `.printdir()` den Inhalt eines Zip-Archivs ausgibt. Dann könnte man aber zumindest einen besseren Namen vergeben - sowohl für die Klasse, als auch für die Methode.
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

snafu hat geschrieben:@Hellstorm: Na, so offensichtlich ist die Antwort nun auch wieder nicht. Zumindest wenn man noch nicht soviel Erfahrung mit Python hat, dann weiß man nicht zwangsläufig, dass man mit Python auch Dateiobjekte erstellen kann, die keiner auf einem Datenträger liegenden Datei zugeordnet sind und dass andere Python-Funktionen diese dann problemlos weiterverarbeiten können. Sowas "darf" man IMHO auch als Anfänger mit Grundkenntnissen ruhig fragen. ;)
Interessant. Da fallen mir einige Anwendungsmöglichkeiten ein, die teil echt nützlich sein könnten. Das iterieren der Datei bzw. sie in chunks aufzuteilen sollte auch möglich sein. Gibt es große Verluste oder Fehler, wenn man eine Datei aufteilt, sie mischt abspeichert und beim einlesen wieder in ihre ursprüngliche Form zsm setzt?
BlackJack

@Dami123: Kannst Du die Frage präzisieren? Ich habe jedenfalls nicht verstanden was Du da eigentlich wissen möchtest.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

snafu hat geschrieben:@Hellstorm: Klassen sollten Tätigkeiten vereinen, die sinnvollerweise zu ihnen passen. Es ist jedoch nicht gerade sinnvoll, dass ein `VirtualFile`-Objekt - also eine Datei - einen Verzeichnisinhalt ausgeben kann. Außerdem nutzen Klassenmethoden normalerweise gemeinsame Instanzattribute (`self.myname`), da man durch Verwendung einer Klasse vermeiden möchte, dass diese Attribute andernfalls als Funktionsargumente herumgereicht werden müssten. Auch diesen Anwendungsfall sehe ich bei dir nicht. Du nutzt hier also eine Klasse, die eine sehr fragwürdige Benutzerschnittstelle anbietet. Man kann sowas natürlich immer machen, aber gerade logisch erscheint es in dem Fall nicht.

EDIT: Ich habe jetzt nicht auf Anhieb geschnallt, dass `.printdir()` den Inhalt eines Zip-Archivs ausgibt. Dann könnte man aber zumindest einen besseren Namen vergeben - sowohl für die Klasse, als auch für die Methode.
Hm, das mit dem printdir() habe ich einfach von der ZipFile-Klasse übernommen. Den Namen fande ich auch komisch, aber ich wollte mir da jetzt auch nichts neues ausdenken. Ob die Funktion da jetzt unbedingt später drin enthalten sein muss, war für mich auch noch offen; vorerst habe ich sie erstmal zum Testen hineingenommen.

Ich möchte später noch die Option einbauen, dass mit dem gnupg-Modul die Dateien nicht nur gezippt, sondern auch verschlüsselt werden können. Daher habe ich das mit der Klasse gemacht. Im jetzigen Fall ist sie noch nicht ganz vollständig. Ich muss auch ehrlich sagen, dass ich noch nicht genau beurteilen soll, wann eine Klasse Sinn macht, und wann einfache Funktionen besser sind.

Sollte ich das dann mit folgenden Funktionen realisieren?

create_zipfile(list_of_files):
Gibt ein BytesIO-Objekt zurück, wo eine Zipdatei enthalten ist.

encrypt_zipfile(virtual_zipfile):
Verschlüsselt die im BytesIO-Objekt enthaltenen Dateien und gibt sie als neues Objekt wieder zurück.
Oder direkt das vorhandene Objekt modifizieren und keine Rückgabe?

Und halt noch mit mehreren Funktionen, wie den Inhalt drucken, das ganze wieder in eine normale Datei schreiben usw?

Da fande ich es doch irgendwie einfacher, eine Klasse zu erstellen, die direkt ein BytesIO-Objekt ist. Da könnte ich dann auch z.B. manuell das Objekt mit ZipFile öffnen. Und zur Bequemlichkeit dann einige Methoden erstellen, so dass ich z.B. dateiobjekt.encrypt() sagen kann, und er dann die Datei verschlüsseln würde.

Wie gesagt, irgendwie habe ich noch Probleme zu entscheiden, wann man lieber etwas als Klasse macht und wann nur mit einfachen Funktionen. Bei vielen Sachen fände ich einfache Funktionen einfacher, aber die werden dann extra als Klasse implementiert, viele andere dann wieder nur als Funktionen.
BlackJack

Was macht denn ``encrypt_zipfile(virtual_zipfile)``? Kann das nur *virtuelle* Zipdateien verschlüsseln? Oder könnte man das nicht auch auf ”normale” ZipFile-Objekte loslassen? Oder gar auf wirklich ganz normale Binärdateien, egal ob die nun ein ZIP-Archiv enthalten oder nicht.

Warum ist eine Klasse einfacher? Da hat man doch zusätzlich noch die Klasse. Und ``dateiobjet.encrypt()`` ist IMHO nicht wirklich einfacher als ``encrypt(dateiobjekt)``.
Antworten