split() Funktion re-implementieren, dass sie iterable ist.

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
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

Hi,

ist es denkbar, dass man die split() Funktion re-implementiert, dass sie iterable ist, statt immer eine komplette Liste zurückgibt?

Hintergrund der Frage ist die Problematik rund um grosse (oder grössere) Eingabedaten, wo es zu bevorzugen wäre, wenn da keine komplette monster-mörder Liste zurückgegeben wird, stattdessen man die einzelnen split() Resultate "häppchenweise" bekommt.

Quasi eine Funktion, welche die Elemente einer split() Funktion einzeln zurück-yieldet.

Ist split() eigentlich in Python implementiert? Wenn ja, wo kann man den source-code davon angucken?
BlackJack

Klar kann man das machen. In einer Schleife mit `index()`-Methode die Trennzeichenfolge suchen und dann die enstprechenden "slices" ``yield``\en. Man kann `index()` auch einen Startoffset mitgeben. `str.split()` dürfte in C implementiert sein. Quelltext gibt's natürlich auf www.python.org :-)
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

Und dann nach split.c suchen?????
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

PS: BITTE KEINE LÖSUNG POSTEN!!!

Ich komme selbst drauf, brauche nur bissle Zeit.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

akis.kapo hat geschrieben:PS: BITTE KEINE LÖSUNG POSTEN!!!
...diese Aussage ist hier eher selten. 8)

.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

Ich bin nicht doof. Nur zu beschäftigt. Nach Feierabend nehme ich mir das Problem vor. Hab schon ne wage Idee wieviele Indizes man mindestens unterscheiden können muss und wie man sie am logischten benennt...
BlackJack

@akis.kapo: Eher nach `stringobject.c` und da dann nach der Funktion `string_split()`. Ist jedenfalls bei der 3.0er Beta dort zu finden.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Das Modul 'itertools' könnte dir helfen.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Also ich würde das so machen:

Code: Alles auswählen

token, rest = data.split(trennzeichen, 1)
Dass in einer Schleife bis rest = '' ist und jedesmal das token yielden.

Gruss

*edit* ups split gibt ja nur 1 Element zurück wenns fertig ist, also halt vorher schauen ob len(res) == 2 ist und daraus entscheiden ob fertig oder noch nicht
BlackJack

Das ist aber mit sehr viel umkopieren von Daten verbunden. Gerade bei grossen Datenmengen und vielen Elementen hat das eine furchtbare Laufzeit.
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

@BlackJack

Hier meine erste Version:
Bitte mal durchchecken und kritisieren.
(Aber nicht zu stark kritisieren, sonst guck ich nicht mehr ins Forum!) :evil:

Dateien:

test1.txt:

Code: Alles auswählen

aaa//bbb//ccc//ddd//eee
test2.txt:

Code: Alles auswählen

Dies ist ein wunderschöner Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline Zeichen '\n\n'.

Dies ist ein bescheuerter Abschnitt,
der durch andere Abschnitte durch einen
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline Zeichen '\n\n'.

Dies ist ein überflüssiger Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline Zeichen '\n\n'.

Dies ist ein bomchickawahwah Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline Zeichen '\n\n'.
itersplit.py:

Code: Alles auswählen

#!/usr/bin/env python

def iterable_split(filename, sep=None, blocksize=None):

        eingabe_band = open(filename,'r') # hier lesen wir
        arbeits_band = '' # hier suchen wir

        if sep is None:
                sep = '\n' # default setzen

        if blocksize is None:
                blocksize = 512 # default setzen
        else:
                blocksize = max(blocksize,1) # bs muss > 0 sein

        current_block = blocksize * '?' # nur spielerei ...

        while not len(current_block) < blocksize: # solange wir "ganze" blöcke lesen ...
                current_block = eingabe_band.read(blocksize) # lese genau einen block
                arbeits_band += current_block # füge ihn ans ende der arbeitsbands
                find_index = 0 # neue suche, neues glück
                while find_index > -1: # solange wir was finden ...
                                find_index = arbeits_band.find(sep) # markiere trefferstartpunkt
                                if find_index > -1: # wenn wir überhaupt treffen ...
                                                treffer = arbeits_band[:find_index] # merke treffer
                                                arbeits_band = arbeits_band[find_index+len(sep):] # lösche treffer+sep vom anfang des arbeitsbands
                                                yield treffer # gib treffer zurück
        if len(arbeits_band) > 0: # falls die dateigrösse in bytes nicht = vielfaches der blockgrösse, gebe "rattenschwanz" zurück
                yield arbeits_band # restgeld zurückgeben

for i in iterable_split('test1.txt', '//', 8):
        print i

for i in iterable_split('test2.txt', '\n\n'):
        print i
EDIT: Ausgabe vergessen, nachgeliefert:

Code: Alles auswählen

$ ./itersplit.py
aaa
bbb
ccc
ddd
eee
Dies ist ein wunderschöner Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline zeichen '\n\n'.
Dies ist ein bescheuerter Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline zeichen '\n\n'.
Dies ist ein überflüssiger Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline zeichen '\n\n'.
Dies ist ein bomchickawahwah Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline zeichen '\n\n'.
Irgendwie erinnert mich der Stil aber an C statt Python. :?
Zuletzt geändert von akis.kapo am Mittwoch 20. Februar 2008, 00:03, insgesamt 2-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mein erster Gedanke war: igitt, Inline-Kommentare!

Der zweite war, dass das ja etwas ganz anderes macht, als ``split()``. Deines splittet eine Datei die es auch selbst öffnet (ohne Fehlerbehandlung). Also übergibtst du split_iterable() gar kein Iterable sondern einen Dateinamen (der zwar auch iterabel ist, aber der wird nicht gesplittet). Worum gehts jetzt? Einen Split-Generator oder eine Datei via Generator zu splitten?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

Es geht darum, split()-Verhalten nachzubauen, aber statt der Rückgabe einer kompletten Liste, die einzelnen Elemente zu yielden.

Ich habe einfach Dateien gewählt in diesem Prototyp, weil ich mir verschiedene Testdateien erstellt habe.
Man hätte auch Strings definieren können statt mit Dateien zu arbeiten.

Es geht ja ums Prinzip. Das mit dem Dateien kann man ja umbauen.

EDIT:
PS: Mein Programm hier hat keine Kommentare. (Ich hab eh alles im Kopf).
Die Kommentare seht nur ihr im Forum in diesem Post.
BlackJack

Die Funktion sähe massiv anders aus, wenn sie sich nicht um die Datei kümmern würde, sondern wirklich nur eine Zeichenkette splitten würde.

Default-Werte sollte man, wenn das möglich ist, gleich in der Argumentliste angeben. Bei `sep` und `blocksize` hätte man das problemlos tun können, weil sowohl Zeichenketten als auch Zahlen "immutable" sind. Da kann es also nicht zu unerwünschten Nebeneffekten kommen. Und eine Blockgrösse kleiner 1 würde ich nicht "heimlich" korrigieren, sondern höchstens dem Aufrufer als Ausnahme um die virtuellen Ohren hauen. Aus dem Zen: "Errors should never pass silently."

Das blockweise "addieren" und dann das Löschen von Treffer und Separator vom `arbeits_band` ist wieder mit sehr viel unnötigem Umkopieren von Zeichenkettendaten verbunden. Du solltest das `iter_split()` (der Name passt besser zu der Funktion) nur auf einer Zeichenkette definieren und wirklich nur die Treffer dort heraus kopieren und ``yield``\en. Wie gesagt, die `find()`-Methode kennt noch mehr Argumente als nur die gesuchte Zeichenkette!

Und wenn man dass dann auf Dateien anwenden will, die richtig gross sind, so bis 2 GiB auf 32-Bit-Systemen, dann kann man anstatt einer Zeichenkette immer noch ein `mmap.mmap()`-Objekt an die Funktion übergeben und braucht sich nicht selbst um so kleinliches blockweises Einlesen kümmern.
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

@BJ

Ok, dann hier der Gegenvorschlag:

Code: Alles auswählen

#!/usr/bin/env python

def iter_split(instring, sep='\n'):
    len_sep = len(sep)
    find_index = 0
    pivot = 0
    while find_index > -1:
        find_index = instring.find(sep, pivot)
        if find_index > -1:
            result = instring[pivot:find_index]
            pivot = find_index + len_sep
            yield result

mystring = 'aaa//bbb//ccc//ddd//eee'

for i in iter_split(mystring, '//'):
    print i
Allerdings ist mir nicht 100%ig klar, wie ich das mache,
dass es per mystring.iter_split(foo, bar) geht, statt als iter_split(string, ...).
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

OK, AB JETZT KÖNNT IHR LÖSUNGEN POSTEN!!!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

akis.kapo hat geschrieben:Allerdings ist mir nicht 100%ig klar, wie ich das mache,
dass es per mystring.iter_split(foo, bar) geht, statt als iter_split(string, ...).
Gar nicht - das ist unterbunden worden (vom BDFL), es ist in Python nicht vorgesehen dass man eingebaute Typen erweitern kann. Was du machen kannst, ist einen neuen Typ zu erstellen und von den eingebauten Typen zu erben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
akis.kapo
User
Beiträge: 127
Registriert: Freitag 1. September 2006, 12:58

Leonidas hat geschrieben:Gar nicht - das ist unterbunden worden (vom BDFL), es ist in Python nicht vorgesehen dass man eingebaute Typen erweitern kann. Was du machen kannst, ist einen neuen Typ zu erstellen und von den eingebauten Typen zu erben.
Hmm, das habe ich mir nämlich fast schon gedacht.
Aber aus einem ganz anderem Grund:

Das mmap() Zeugs kann man ja nicht innerhalb der iter_strip() Funktion einbinden,
weil die ja auf einem fertigem String arbeitet (ob als Parameter übergeben oder auf Objekt angewandt, das sei mal hingestellt.).
Das bedeutet auch, dass das "neue" Objekt, dass von string erben soll, irgendwie ne classenmethode oder sonst einen Weg hat,
versteckt vom iter_split() irgendwas zu mmappen, oder sonstwas, welches blockweises lesen ermöglicht.

@BJ
Dann würde ich aber die manuelle Blockweise-Lesen Lösung bevorzugen,
weil mir gehts ja um Speicherersparnis bei riesigen Dateien.
Mmap, so wie ich das verstanden habe, speichert ja alles Doppelt.
Sprich, das Delta im Arbeitsspeicher (ist mutable und kann sich vom Dateiinhalt unterscheiden, solange man nicht closed oder flushed)
und dann noch das nicht unterbindbare Filecache vom OS, welches
das gelesene File cached (unverändert, bzw. halt so, wies gelesen wird.).

Oder nicht, oder was meint ihr?

EDIT:

Ist der nicht unterbindbare Filecache vom OS eigentlich wirklich so ununterbindbar?
Ich weiss von diversen Datenbanksystemen, dass sie für ihre SMS das OS Filecaching "deaktivieren"
(durch nen bestimmten open() Parameter, der das caching unterbindet?).
IBMs AIX soll das können. Die Frage ist kanns jedes Unix/Linux? (Oder gar Windows)
Und wenn ja, wo gibts diese Option in python? (ich seh da nix unter open oder file)
BlackJack

Ich verstehe Deine Bedenken bezüglich `mmap` nicht ganz. Du veränderst die Zeichenkette in `iter_split()` ja nicht, also muss `mmap` sich da auch keine Veränderungen im Speicher merken. Da wird nichts doppelt gespeichert, ausser die Kopien, die Du heraus "sliced", aber das hast Du bei jeder anderen Lösung auch. `mmap` dürfte wesentlich effizienter sein, als manuelles blockweises Lesen, das ist schliesslich *der* Mechanismus auf dem in der Regel die ganze virtuelle Speicherverwaltung vom Betriebssystem aufbaut.

Ebenfalls Edit: Wieso willst Du den Block-Cache unterbinden? Sowie dort Platz gebraucht wird, fliegen die Blöcke von dem `mmap` halt raus. Stört doch nicht weiter. Sofern sie dort überhaupt landen ─ wäre eigentlich unsinnig.
Antworten