Problem beim Komprimieren mit bz2

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
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Hallo zusammen,

ich möchte gerne eine bestehende Datei mit bzip2 komprimieren. Dazu wollte ich das bz2-Modul verwenden. Folgendes habe ich versucht:
(in Python 2.4)

Code: Alles auswählen

compressor = bz2.BZ2Compressor()
compressor.compress('Datei.txt')
compressor.flush()
Es gibt keine Fehlermeldung, aber das Prg macht auch nichts.

Wahrscheinlich verwende ich das Modul falsch. Aber wie müsste es richtig sein?

CU
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Da wird bloß der String "datei.txt" gebz2't und der Rückgabewert gleich danach weggeschmissen.

Das Modul hat eine eigenständige compress function --> Datei lesen, bz2.compress anwenden, neuen string in datei schreiben, file.close().
lunar

@str1442:
bz2.BZ2File existiert:

Code: Alles auswählen

from bz2 import BZ2File
from shutil import copyfileobj
from contextlib import nested, closing

with nested(open('source', 'rb'), closing(BZ2File('target.bz2', 'wb'))) as (source, target):
    copyfileobj(source, target)
Für das Packen eines Bytestrings braucht es kein "bz2"-Import:

Code: Alles auswählen

'data'.encode('bz2')
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Hallo zusammen,

das Beispiel mit dem nested... funktioniert nicht, da ich Python 2.4 verwende. "contextlib" gibt es nicht.

Ich hab jetzt folgendes versucht, was aber immer noch nicht richtig zu sein scheint. Kann mir da mal jemand in Python-Code ein Beispiel geben?

Code: Alles auswählen

        bzip2_source = all_logfiles[all_logfiles_row][0]
        bzip2_target = all_logfiles[all_logfiles_row][0] + '.bz2'

        bzip2_handle = open(bzip2_source, 'r')
        bzip2_handle.readlines()
        btest = bz2.compress(bzip2_handle)
        bzip2_handle.write(btest)
        bzip2_handle.close()
(Wobei in all_logfiles[all_logfiles_row][0] ein Dateiname steht)

CU
lunar

Warum nur willst du unbedingt "bz2.compress" verwenden?! Obigen Code kann man auch ohne with-statement nutzen:

Code: Alles auswählen

source = open(filename, 'rb')
try:
    target = BZ2File(filename + os.extsep + 'bz2', 'wb')
    try:
        copyfileobj(source, target)
    finally:
        target.close()
finally:
    source.close()
Das geschachtelte try-finally-Konstrukt ist nötig, da auch ".close()" eine Ausnahme auslösen kann.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Hallo lunar,

Nein, ich will ja gar nicht unbedingt bz2.compress verwenden. Es erschien mir nur als einzige Lösung.

Aber dein Beispiel ist genau das, was ich gesucht bzw. versucht habe... Besser gehts gar nicht!! :D

Danke dir...
lunar

Nur noch zu zum Schluss: Update sobald als möglich auf Python 2.5 oder am besten gleich 2.6. Gerade das with-Statement ist schon eine extrem coole Sache.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

lunar:

Wozu brauchts bei dem with Block die Anwendung von contextlib.closing()? Sollte die Datei nicht (da with statement) automatisch geschlossen werden?
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Kann es sein, dass closing grundsätzlich benutzt werden muss, wenn ein with-Statement in Verbindung mit einer Funktion genutzt werden soll, die Teil eines *Moduls* ist (also nicht builtin)? Bei urlopen verhält es sich ja ganz ähnlich wie im vorliegenden Beispiel. Denn wenn ich yield richtig verstehe - und das ist ja ein ganz wesentlicher Teil von closing - verleiht es einer Funktion ähnlich wie einem Dateiobjekt die Eigenschaft schliessbar zu sein, richtig? Also es wird sozusagen der Stream überwacht und auf jeden Fall geschlossen, wenn etwas schiefgeht. Soll heißen: Auch wenn ich die Kontrolle eines dateiähnlichen Objekts an eine andere Instanz übergebe und dann eigentlich nur noch Zugriff auf die Instanz habe (die ja in den meisten Fällen keine spezielle close-Methode besitzt), habe ich mittels closing eben doch die Kontrolle. Irgendwie so. ^^
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Denn wenn ich yield richtig verstehe - und das ist ja ein ganz wesentlicher Teil von closing - verleiht es einer Funktion ähnlich wie einem Dateiobjekt die Eigenschaft schliessbar zu sein, richtig?
Durch Benutzung von yield statt return in einer Funktion wird einfach ein Generator aus eben jener Funktion gemacht. Soll heißen, ein standarisiertes Objekt mit genauso standardrisierten Methoden das zum Iterieren dient und je nach Position der verschiedenen yield Statements einen Wert zurückgibt, bis es leer ist und StopIteration "raist".

Ich hab mir den Code der contextlib grad kurz angeschaut, closing() ist bloß eine Klasse die das Standard-with Interface bietet und beim Aufruf der __exit__ Methode die Objekteigene close() Methode aufruft. Im Grunde dürfte closing() also nur nötig sein, um ein Standard-with-Interface über ein Objekt drüber zu wrappen, wenn selbiges das nicht bereits selbst mitbringt - wobei ich meine, das müsste doch zumindest bei allen Modulen der Stdlib standardmäßig der Fall sein. So hatte ich die closing() auch verstanden - Das bz2 Modul ist eine Shared Library, also in C geschrieben, und wenn es (wie auch immer das bei eingebundenen C Modulen funktioniert) keine eigene __exit__ Methode bietet, muss man closing() anwenden.
lunar

@snafu
Wo siehst du denn jetzt ein "yield"?!

yield hat mit einem Context-Manager per se erstmal nichts zu tun, sondern dient zu Implementierung von Generator-Funktionen. Ein Zusammenhang besteht nur, wenn man einen Generator mit contextlib.contextmanager() nutzt. "yield" macht aber auf keinen Fall eine Funktion "schließbar" (was immer das auch sein soll).

Auch hat ein Context-Manager per se nichts mit Streams zu tun. Context-Manager kann man für alles mögliche implementieren, z.B. auch für Locks. Bei der Arbeit mit readline sind sie auch manchmal ganz hilfreich.

Irgendwie hast du den Sinn, Zweck und Implementierung des with-Statements noch nicht so ganz verstanden ;)

@str1442
bz2.BZ2File implementiert das Context-Manager-Protokoll nicht (zumindest in Python 2.5). Das gilt für viele andere Module aus der Standardbibliothek auch (z.B. urllib, urllib2, StringIO, etc.).
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich hatte das deshalb gedacht:
closing(thing)
Return a context manager that closes thing upon completion of the block. This is basically equivalent to:

Code: Alles auswählen

from contextlib import contextmanager

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()
And lets you write code like this:

Code: Alles auswählen

from __future__ import with_statement
from contextlib import closing
import urllib

with closing(urllib.urlopen('http://www.python.org')) as page:
    for line in page:
        print line
without needing to explicitly close page. Even if an error occurs, page.close() will be called when the with block is exited.
http://www.python.org/doc/2.5.2/lib/mod ... xtlib.html

Und dann habe ich in die Doku zu yield geguckt, die ich aber nicht wirklich verstanden habe und mir meinen eigenen Reim draus gemacht.
lunar

Ach so ... abgesehen davon, dass das nicht die eigentliche Implementierung von closing() ist, ist yield hier auch nichts besonderes.

Die Funktion ist eine normale Generator-Funktion, die ein Generator-Objekt zurückgibt. Der Dekorator erzeugt daraus einen Context-Manager, der beim Betreten des Blocks ".next()" des Generators. Die Funktion läuft bis zum yield-Statement durch, welches dann den Wert zurückgibt, der per "as" gebunden werden kann. Dann wird er with-Block ausgeführt. Danach ruft der Context-Manager eneut ".next()" auf, was den Rest der Funktion ausführt.

Mittels eines Generators und des contextmanager-Dekorators kann man einfache Context-Manager implementieren, ohne gleich eine Klasse entwickeln zu müssen.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sowas in der Art meinte ich als ich vom "Schliessen" der Funktion sprach. Also die Funktion wird quasi in 3 Teile gespalten: Vor dem Zugriff auf's übergebene Objekte, beim Zugriff auf das Objekt (gekennzeichnet durch yield) und nach dem Zugriff. Und wenn in dem Teil des Zugriffs etwas schiefgeht, dann meldet sich yield und der Rest der Funktion wird nicht mehr ausgeführt, richtig?

Also demnach: Iteriere immer weiter. Wenn etwas in der yield-Zeile nicht klappt -> StopIteration

Wobei mir noch schleierhaft ist, wozu man sowas braucht, denn würde die Funktion nicht ohnehin eine Exception werfen und abbrechen?
lunar

Nein, du hast den Zusammenhang zwischen dem Context-Manager und dem Generator nicht verstanden.

Der "Rest" der Funktion wird in jedem Fall ausgeführt, egal auf welche Weise der With-Block verlassen wird. Das gerade kennzeichnet ja einen Kontext-Manager, ansonsten wäre die ganze Sache ja ziemlich sinnlos.

Und yield "meldet sich auch" nicht, wenn etwas schiefgeht. yield sorgt überhaupt dafür, dass die Funktion die Kontrolle an den Aufrufer zurückgegeben wird, so dass der with-Block ausgeführt werden kann.

Lies doch mal in der Doku, was ein Kontext-Manager ist, was ein Generator ist, wozu das yield-Statement da ist, und wie der contextmanager-Dekorator funktioniert.
Antworten