Problem bei Wertübergabe in Funktion

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.
BlackJack

@Nobuddy: Überlege Dir mal wie oft Du `re.compile()` mit immer wieder dem selben regulären Ausdruck aufrufst. Da kommt doch bei dem gleichen Ausdruck auch immer wieder ein äquivalentes Objekt als Ergebnis. Also ist es unnötig das ständig aufs neue zu berechnen. Das sollte man *einmal* machen und dann das Ergebnis davon verwenden. Und dann vielleicht auch in `find_digits()` direkt die `search()`-Methode auf dem Objekt aufrufen statt `re.search()` zu verwenden. Das macht ja letztendlich auch nichts anderes als seinerseits dann noch mal die Methode auf dem Objekt aufzurufen. Diese Indirektion kann man sich sparen.

In `find_digits()` verwendest Du `regexp` ohne das man einfach nachvollziehen kann, wo der Wert dafür eigentlich her kommt. Man muss die komplette `get_inhalt()`-Funktion verstehen um zu sehen wo und wie der Name dort an einen oder mehrere Werte gebunden wird. Deshalb würde ich den regulären Ausdruck dort als Argument übergeben.

Die Funktion finde ich aber sowieso etwas übertrieben. Wenn man das „inline” schreibt und statt der Behandlung des `AttributError` das Ergebnis der Suche an einen Namen bindet und damit dann nur weiter macht wenn das Ergebnis nicht `None` ist, kann man das ganze kürzer schreiben und sich auch den unnötigen doppelten Aufruf von `find_digits()` sparen.

Zu den Gruppennamen: Schau Dir doch bitte mal an was das Wörterbuch enthält. Ein Wörterbuch in eine Liste mit Tupeln aus Schlüssel/Wert-Paare umzuwandeln um dann per verschachtelten Indexzugriff auf einen Wert zuzugreifen ist umständlich und wie gesagt auch *falsch*! Wenn das bei Dir funktioniert, dann ist das reiner Zufall, denn Wörterbücher haben keine Ordnung. Du fragst mit Deinem Code *irgendeinen* zufälligen Wert ab. Das würde nur funktionieren wenn das Wörterbuch nur einen Wert enthalten würde, aber dann wäre die Datenstruktur und der umständliche Zugriff noch unsinniger.

Der Schleifenkörper ist immer noch viel zu aufgebläht. Wenn etwas am Anfang oder Ende von *jedem* möglichen Zweig passiert, dann muss man das nicht in *jeden* Zweig reinschreiben, sondern *einmal* an passender Stelle. ``daten.append(tuple(r))`` wird in jedem Fall (fast) am Ende der Ausführung eines Schleifendurchlaufs gemacht. Das kann man deshalb auch *einmal* am Ende der Schleife machen. Damit sparst Du zwei ``else``-Zweige. Genau so kann man das erhöhen von `wert` aus dem ``if``/``else`` heraus ziehen. Und von dem Konstrukt bleibt auch nur noch das ``if`` wenn man mal überlegt was denn schlimmes passiert wenn man bei der '1' Tausenderzeichen entfernt.

`wert` ist übrigens ein ziemlich schlechter Name, weil nichtssagend. Denn ein Wert ist letztendlich *alles* was man an einen Namen binden kann. Namen sollten beschreiben wofür der daran gebundene Wert steht. In diesem Fall zum Beispiel scheint das die Anzahl der veränderten Datensätze zu sein.

Man könnte das wie folgt eindampfen (ungetestet):

Code: Alles auswählen

def whatever(filename, beschreibung, ve, inhalt, suchmuster):
    regexp = re.compile(suchmuster, re.U | re.I)
    changes = False
    daten = set()
    with open(filename, 'r') as infile:
        for row in my_reader(infile):
            if row[ve] == 'VE' and row[inhalt] == '1':
                match = regexp.search(row[beschreibung])
                if match:
                    row[inhalt] = (match.group('VALUE') or '1').replace('.', '')
                    changes = True
            
            daten.add(tuple(row))

    if changes:
        write_csv(filename, sorted(daten))
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack,
BlackJack hat geschrieben:@Nobuddy: Überlege Dir mal wie oft Du `re.compile()` mit immer wieder dem selben regulären Ausdruck aufrufst. Da kommt doch bei dem gleichen Ausdruck auch immer wieder ein äquivalentes Objekt als Ergebnis. Also ist es unnötig das ständig aufs neue zu berechnen. Das sollte man *einmal* machen und dann das Ergebnis davon verwenden. Und dann vielleicht auch in `find_digits()` direkt die `search()`-Methode auf dem Objekt aufrufen statt `re.search()` zu verwenden. Das macht ja letztendlich auch nichts anderes als seinerseits dann noch mal die Methode auf dem Objekt aufzurufen. Diese Indirektion kann man sich sparen.
Das war mir so nicht bewußt, danke für den Tip!
BlackJack hat geschrieben:In `find_digits()` verwendest Du `regexp` ohne das man einfach nachvollziehen kann, wo der Wert dafür eigentlich her kommt. Man muss die komplette `get_inhalt()`-Funktion verstehen um zu sehen wo und wie der Name dort an einen oder mehrere Werte gebunden wird. Deshalb würde ich den regulären Ausdruck dort als Argument übergeben.

Die Funktion finde ich aber sowieso etwas übertrieben. Wenn man das „inline” schreibt und statt der Behandlung des `AttributError` das Ergebnis der Suche an einen Namen bindet und damit dann nur weiter macht wenn das Ergebnis nicht `None` ist, kann man das ganze kürzer schreiben und sich auch den unnötigen doppelten Aufruf von `find_digits()` sparen.
Da habe ich noch einiges zu lernen.
BlackJack hat geschrieben:Zu den Gruppennamen: Schau Dir doch bitte mal an was das Wörterbuch enthält. Ein Wörterbuch in eine Liste mit Tupeln aus Schlüssel/Wert-Paare umzuwandeln um dann per verschachtelten Indexzugriff auf einen Wert zuzugreifen ist umständlich und wie gesagt auch *falsch*! Wenn das bei Dir funktioniert, dann ist das reiner Zufall, denn Wörterbücher haben keine Ordnung. Du fragst mit Deinem Code *irgendeinen* zufälligen Wert ab. Das würde nur funktionieren wenn das Wörterbuch nur einen Wert enthalten würde, aber dann wäre die Datenstruktur und der umständliche Zugriff noch unsinniger.
Es ist ja eine TAB-getrennte Textdatei und die Datensätze werden ja zeilenweise abgearbeitet, egal ob die nun sortiert sind oder nicht. Wenn ich nun eine Bedingung voraussetze, so betrifft es ja alles Datensätze gleichermaßen, entweder TRUE oder FALSE.
Oder liege ich da falsch und verstehe Deine Message nicht?
BlackJack hat geschrieben:Der Schleifenkörper ist immer noch viel zu aufgebläht. Wenn etwas am Anfang oder Ende von *jedem* möglichen Zweig passiert, dann muss man das nicht in *jeden* Zweig reinschreiben, sondern *einmal* an passender Stelle. ``daten.append(tuple(r))`` wird in jedem Fall (fast) am Ende der Ausführung eines Schleifendurchlaufs gemacht. Das kann man deshalb auch *einmal* am Ende der Schleife machen. Damit sparst Du zwei ``else``-Zweige. Genau so kann man das erhöhen von `wert` aus dem ``if``/``else`` heraus ziehen. Und von dem Konstrukt bleibt auch nur noch das ``if`` wenn man mal überlegt was denn schlimmes passiert wenn man bei der '1' Tausenderzeichen entfernt.
Einzigstes Problem ist, daß ja nur die Daten in Datei zurückgeschrieben werden, die Wahr sind. Ich benötige ja aber beide TRUE und FALSE, da der Datensatz von FALSE ja nicht wirklich falsch ist, sondern nur nicht in dieser Funktion bearbeitet wird.
BlackJack hat geschrieben:`wert` ist übrigens ein ziemlich schlechter Name, weil nichtssagend. Denn ein Wert ist letztendlich *alles* was man an einen Namen binden kann. Namen sollten beschreiben wofür der daran gebundene Wert steht. In diesem Fall zum Beispiel scheint das die Anzahl der veränderten Datensätze zu sein.
'wert' ist nur ein Zähler für mich, um feststellen zu können, ob Datensätze bearbeitet wurden. Sind keine Datensätze bearbeitet worden, muß ja auch die Datei nicht mit den gleichen Daten überschrieben werden.
BlackJack hat geschrieben:Man könnte das wie folgt eindampfen (ungetestet):

Code: Alles auswählen

def whatever(filename, beschreibung, ve, inhalt, suchmuster):
    regexp = re.compile(suchmuster, re.U | re.I)
    changes = False
    daten = set()
    with open(filename, 'r') as infile:
        for row in my_reader(infile):
            if row[ve] == 'VE' and row[inhalt] == '1':
                match = regexp.search(row[beschreibung])
                if match:
                    row[inhalt] = (match.group('VALUE') or '1').replace('.', '')
                    changes = True
            
            daten.add(tuple(row))

    if changes:
        write_csv(filename, sorted(daten))
Das sieht gut aus, müßte nur noch die Daten, die nicht 'changes = True' und auch nicht 'if row[ve] == 'VE' and row[inhalt] == '1':' sind, wieder in die Datei zusammen zurück schreiben.
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, Dein Konstrukt funktioniert und meine Befürchtung, daß nicht alle Daten (TRUE + FALSE) in die Datei zurück geschrieben wird, war umsonst. Dies verwirrt mich etwas, da es doch heißt 'if chance:', also 'if True' oder nicht?
Das würde mich interessieren.

Was mir noch nicht klar ist, auch wenn es hier evtl. schon einmal erklärt worden ist: Worin besteht genau der Unterschied zwischen 'daten.add' und 'daten.append'?
BlackJack

@Nobuddy: Bei dem Wörterbuch verstehst Du meine Aussage offensichtlich wirklich nicht. Es geht nicht um die Datensätze als ganzes und ob die sortiert sind, oder nicht, sondern um diesen Ausdruck: ``find_digits(r[beschreibung]).items()[0][1]``. `find_digits()` gibt ein Wörterbuch zurück, und zwar mit so vielen Einträgen wie der reguläre Ausdruck benannte Gruppen hatte. Also bei den bisher gezeigten Ausdrücken sind das zwei. Wenn Du dir eine Liste mit den Schlüssel/Wert-Paaren mit `items()` geben lässt, dann gibt es keinerlei Garantien in welcher Reihenfolge die sind. Du nimmst also einen zufälligen der beiden Werte und arbeitest damit weiter. Nun kann ich mir aber nicht vorstellen, dass es egal ist ob Du den Wert für 'VALUE' oder den Wert für 'UNIT' im weiteren verwendest.

Ich verstehe nicht genau was Du mit es würden „ja nur die Daten in Datei zurückgeschrieben werden, die Wahr sind” meinst, denn Dein Code hängt das Tupel, das aus `r` gebildet wird, in *jedem* möglichen Ablaufpfad an `daten` an. Es gibt keine Kombination von Bedingungen bei der das *nicht* passiert. Falls Du meinst, dass es eine solche gäbe, dann nenne doch mal ein Beispiel für ein `r` das nicht zu `daten` hinzugefügt würde‽

Was der Wert von `wert` bedeuten soll habe ich also richtig erkannt. Es wäre halt bloss schön wenn man das schon am Namen erkennen würde und nicht zwingend nachschauen müsste was daran im Programmverlauf gebunden wird, und wie der Wert am Ende verwendet wird. ;-)

Wieso sollte ``if changes:`` das selbe sein wie ``if True:``? Am Anfang – zweite Zeile der Funktion — wird der Name an `False` gebunden. Und an `True` nur, wenn der Programmverlauf mindestens einmal bis zum innersten ``if`` in der Schleife vordringen kann. Was er nur tut, wenn auch in der Zeile davor tatsächlich der Datensatz verändert wurde.

`daten.append()` funktioniert nicht wenn man `daten` an einen Wert vom Typ `set` gebunden hat. Und `daten.add()` funktioniert nicht bei Listen. :-)
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack,
BlackJack hat geschrieben:@Nobuddy: Bei dem Wörterbuch verstehst Du meine Aussage offensichtlich wirklich nicht. Es geht nicht um die Datensätze als ganzes und ob die sortiert sind, oder nicht, sondern um diesen Ausdruck: ``find_digits(r[beschreibung]).items()[0][1]``. `find_digits()` gibt ein Wörterbuch zurück, und zwar mit so vielen Einträgen wie der reguläre Ausdruck benannte Gruppen hatte. Also bei den bisher gezeigten Ausdrücken sind das zwei. Wenn Du dir eine Liste mit den Schlüssel/Wert-Paaren mit `items()` geben lässt, dann gibt es keinerlei Garantien in welcher Reihenfolge die sind. Du nimmst also einen zufälligen der beiden Werte und arbeitest damit weiter. Nun kann ich mir aber nicht vorstellen, dass es egal ist ob Du den Wert für 'VALUE' oder den Wert für 'UNIT' im weiteren verwendest.
Ich versuche das zu verstehen und glaube, daß der Knoten evtl. geplatzt sein könnte. Verstehe ich das richtig, daß je nach dem wie ich die RegExp aufgebaut habe, in einem Fall 'VALUE' den richtigen Wert hat und bei der nächsten RegExp, 'UNIT' den richtigen Wert hat?
BlackJack hat geschrieben:Ich verstehe nicht genau was Du mit es würden „ja nur die Daten in Datei zurückgeschrieben werden, die Wahr sind” meinst, denn Dein Code hängt das Tupel, das aus `r` gebildet wird, in *jedem* möglichen Ablaufpfad an `daten` an. Es gibt keine Kombination von Bedingungen bei der das *nicht* passiert. Falls Du meinst, dass es eine solche gäbe, dann nenne doch mal ein Beispiel für ein `r` das nicht zu `daten` hinzugefügt würde‽
Da komme ich evtl. später drauf zurück.
BlackJack hat geschrieben:Was der Wert von `wert` bedeuten soll habe ich also richtig erkannt. Es wäre halt bloss schön wenn man das schon am Namen erkennen würde und nicht zwingend nachschauen müsste was daran im Programmverlauf gebunden wird, und wie der Wert am Ende verwendet wird. ;-)
Ok, ich benenne jetzt `wert` in 'zaehler' um.
Diesen Zähler positioniere ich genau an den Stellen, bei denen Daten geändert werden. Ist zwar nicht das Gleiche wie bei Dir 'chance = True', aber hat doch eine gewisse Ähnlichkeit, da ich nur dann die Datei neu beschreibe, wenn auch wirklich Daten geändert wurden. Gleichzeitig kann ich den Zähler auch für eine print-Ausgabe nutzen, z.B. ''print('Es wurden ' + str(zaehler) + ' Änderungen vorgenommen')''.
BlackJack hat geschrieben:Wieso sollte ``if changes:`` das selbe sein wie ``if True:``? Am Anfang – zweite Zeile der Funktion — wird der Name an `False` gebunden. Und an `True` nur, wenn der Programmverlauf mindestens einmal bis zum innersten ``if`` in der Schleife vordringen kann. Was er nur tut, wenn auch in der Zeile davor tatsächlich der Datensatz verändert wurde.

`daten.append()` funktioniert nicht wenn man `daten` an einen Wert vom Typ `set` gebunden hat. Und `daten.add()` funktioniert nicht bei Listen. :-)
Das habe ich jetzt verstanden, Danke! :wink:
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, habe mich mit Deinem Konstrukt beschäftigt, welches ja auch funktioniert.
Wenn ich aber versuche dieses zu erweitern, um weitere Abfrage-Definitionen verwenden zu können, bekomme ich das nicht so hin, daß es 100-prozentig funktioniert.
Deshalb habe ich mein Konstrukt verwendet und teilweise Deine Vorschläge integriert.
Der erste Teil mit dem ich dann das Konstrukt steuere, sieht so aus:

Code: Alles auswählen

from b_test import get_inhalt, get_stueck
# suche nach 'GEPACKT ZU [Zahlenwert]' (Einheiten) 048
get_inhalt(base_daten_path, 3, 6, 7, r"GEPACKT ZU ?(?P<VALUE>\d*\.?\d*).?\s*(?P<UNIT>\w*)")
# suche nach 'INHALT: [Zahlenwert]*ST' (Stück) 048
get_inhalt(base_daten_path, 3, 6, 7, r"INHALT: ?(?P<VALUE>\d*\.?\d*).?ST\s*(?P<UNIT>\w*)")
# suche nach 'INHALT: [Zahlenwert]*ET' (Etiketten) 048
get_inhalt(base_daten_path, 3, 6, 7, r"INHALT: ?(?P<VALUE>\d*\.?\d*).?ET\s*(?P<UNIT>\w*)")
# suche nach 'INHALT: [Zahlenwert]*BL' (Blatt) 048
get_inhalt(base_daten_path, 3, 6, 7, r"INHALT: ?(?P<VALUE>\d*\.?\d*).?BL\s*(?P<UNIT>\w*)")
# suche nach 'VE*[Zahlenwert]' (VE=123, VE123) 028
get_inhalt(base_daten_path, 3, 6, 7, r"VE(?P<VALUE>\d*)\s*(?P<UNIT>\w*)")
# suche nach '[Zahlenwert]*ST' (Stück) 028
get_inhalt(base_daten_path, 3, 6, 7, r"(?P<VALUE>\d*)ST\s*(?P<UNIT>\w*)")
# suche nach '[Zahlenwert]*ST' (Stück) 120
get_inhalt(base_daten_path, 3, 6, 7, r"(?P<VALUE>\d*\.?\d*).?ST\s*(?P<UNIT>\w*)")
# suche nach '[Zahlenwert]*BL' (Blatt) 120
get_inhalt(base_daten_path, 3, 6, 7, r"(?P<VALUE>\d*)BL\s*(?P<UNIT>\w*)")
#
# Wenn VE ist STÜCK und Inhalt ist nicht 1
get_stueck(base_daten_path, 6, 7)
Und das Konstrukt dazu so:

Code: Alles auswählen

def get_inhalt(filename, beschreibung, ve, inhalt, suchmuster):

    # RegExp, Verarbeitung von Inhaltsangaben
    regexp = re.compile(suchmuster, re.U|re.I)

    zaehler = 0
    daten = set()
    with open(filename, "r") as infile:
        for row in my_reader(infile):
            if row[ve] == 'VE' and row[inhalt] == '1':
                zahlenwert = regexp.search(row[beschreibung])
                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
                    if row[inhalt] != '1':
                        daten.add(tuple(row))
                        zaehler += 1
                    else:
                        daten.add(tuple(row))
                else:
                    daten.add(tuple(row))

            elif row[ve] == 'STÜCK':
                zahlenwert = regexp.search(row[beschreibung])
                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
                    if row[inhalt] != '1':
                        row[ve] = 'VE'
                        daten.add(tuple(row))
                        zaehler += 1
                    else:
                        daten.add(tuple(row))
                else:
                    daten.add(tuple(row))
            else:
                daten.add(tuple(row))

    if zaehler > 0:
        write_csv(filename, sorted(daten))
        if zaehler > 1:
            info(str(zaehler) + ' Datensätze aktualisiert')
        else:
            info(str(zaehler) + ' Datensatz aktualisiert')
    else:
        info('Überprüfung abgeschlossen.')



def get_stueck(filename, ve, inhalt):

    zaehler = 0
    daten = set()
    with open(filename, "r") as infile:
        for row in  my_reader(infile):
            if row[ve] == 'STÜCK' and row[inhalt] != '1':
                row[inhalt] = '1'
                daten.add(tuple(row))
                zaehler += 1
            else:
                daten.add(tuple(row))

    if zaehler > 0:
        write_csv(filename, sorted(daten))
        if zaehler > 1:
            info(str(zaehler) + ' Datensätze aktualisiert')
        else:
            info(str(zaehler) + ' Datensatz aktualisiert')
    else:
        info('Überprüfung abgeschlossen.')
BlackJack

@Nobuddy: Ich fürchte der Knoten ist noch nicht geplatzt. Welche Gruppe den gewünschten Wert enthält, hängt zwar natürlich tatsächlich davon ab wie Du den Ausdruck aufbaust, aber das wirst Du ja hoffentlich so tun, dass die Zuordnung Sinn ergibt. Das ist aber nicht das Problem. Das Problem ist, das Wörterbücher ungeordnet sind, und `items()` eine Liste mit den Schlüssel/Wert-Paaren in einer *beliebigen* Reihenfolge liefert. Das heisst wenn es die Schlüssel 'UNIT' und 'VALUE' gibt, kann es sein das entweder 'UNIT' oder 'VALUE' zuerst in der Liste steht. Du nimmst aber immer das erste Element. Man kann also gar nicht voraus sagen welchen der beiden Werte Du da entnimmst und dann damit weiter arbeitest.

Bei den ganzen wiederholten `get_inhalt()`-Aufrufen ändert sich ja nur der reguläre Ausdruck, die könnte man also in eine Liste herausziehen und die Aufrufe dann in einer Schleife abarbeiten. Ansonsten könnte man da eventuell auch Ausdrücke zusammenfassen.

Du hast da jetzt wieder ``daten.add(tuple(row))`` sieben mal zu oft im Quelltext. Wenn das wirklich in jedem möglichen Programmzweig vorkommt, dann reicht es das nur an einer Stelle zu machen. Und das sich im ``elif`` in `get_inhalt()` Quelltext wiederholt, der auch im ``if``-Teil steht, deutet sehr stark darauf hin, dass der Entscheidungsbaum ungünstig aufgebaut ist.
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

BlackJack hat geschrieben:@Nobuddy: Ich fürchte der Knoten ist noch nicht geplatzt. Welche Gruppe den gewünschten Wert enthält, hängt zwar natürlich tatsächlich davon ab wie Du den Ausdruck aufbaust, aber das wirst Du ja hoffentlich so tun, dass die Zuordnung Sinn ergibt. Das ist aber nicht das Problem. Das Problem ist, das Wörterbücher ungeordnet sind, und `items()` eine Liste mit den Schlüssel/Wert-Paaren in einer *beliebigen* Reihenfolge liefert. Das heisst wenn es die Schlüssel 'UNIT' und 'VALUE' gibt, kann es sein das entweder 'UNIT' oder 'VALUE' zuerst in der Liste steht. Du nimmst aber immer das erste Element. Man kann also gar nicht voraus sagen welchen der beiden Werte Du da entnimmst und dann damit weiter arbeitest.
Ok, das ist gar nicht so einfach, wie ich es gedacht habe, habe es aber jetzt verstanden.
BlackJack hat geschrieben:Bei den ganzen wiederholten `get_inhalt()`-Aufrufen ändert sich ja nur der reguläre Ausdruck, die könnte man also in eine Liste herausziehen und die Aufrufe dann in einer Schleife abarbeiten. Ansonsten könnte man da eventuell auch Ausdrücke zusammenfassen.
Das habe ich auch zuerst versucht. Ich hatte die Ausdrücke in eine txt-Datei gepackt, welches aber dann beim Aufruf in einer Schleife nicht funktionierte. Hyperion hat mir da dann empfohlen, die Ausdrücke in einer python-Datei zu erstellen und diese dann in das Script einzubinden, was ich aber noch nicht umgesetzt habe, da zuerst das Konstrukt richtig funktionieren muß.
BlackJack hat geschrieben:Du hast da jetzt wieder ``daten.add(tuple(row))`` sieben mal zu oft im Quelltext. Wenn das wirklich in jedem möglichen Programmzweig vorkommt, dann reicht es das nur an einer Stelle zu machen. Und das sich im ``elif`` in `get_inhalt()` Quelltext wiederholt, der auch im ``if``-Teil steht, deutet sehr stark darauf hin, dass der Entscheidungsbaum ungünstig aufgebaut ist.
Daß Du das bemängelst, war mir klar, habe aber noch keine andere Lösung gefunden, da sonst ein großer Teil der Daten der Datei nicht wieder in die Datei zurück geschrieben wird.
Bei ``if`` und ``elif`` verstehe ich das so. Die Datensätze der Datei werden ja zeilenweise abgearbeitet. Jetz besteht die Möglichkeit, daß der Datensatz die ``if``-Anweisung erfüllt (wenn row[ve] == 'VE' und row[inhalt] == '1'). Wenn dies nicht zutrifft könnte es auch sein, daß die ``elif``-Anweisung dies (row[ve] == 'STÜCK') erfüllt, dann wird dort nach einer Inhaltsangabe in der Beschreibung gesucht.
Ganz am Schluß überprüfe ich mit get_stueck, Datensätze die dies (wenn row[ve] == 'STÜCK' und row[inhalt] != '1') erfüllen.
BlackJack

@Nobuddy: Okay, machen wir die Umformung mal Schritt für Schritt für den Schleifenkörper in `get_inhalt()`. Fangen wir von Innen her beim ersten innersten ``if`` an, also das dritte nach dem ``for``:

Code: Alles auswählen

                    if row[inhalt] != '1':
                        daten.add(tuple(row))
                        zaehler += 1
                    else:
                        daten.add(tuple(row))
Egal ob die Bedingung zutrifft oder nicht, `row` wird `daten` hinzugefügt. Ist für Dich nachvollziehbar, dass das deshalb das gleiche macht wie das folgende?

Code: Alles auswählen

                    if row[inhalt] != '1':
                        zaehler += 1
                    daten.add(tuple(row))
Wenn man das im Quelltext einsetzt und sich dann das nächsthöhere ``if`` anschaut…

Code: Alles auswählen

                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
                    if row[inhalt] != '1':
                        zaehler += 1
                    daten.add(tuple(row))
                else:
                    daten.add(tuple(row))
…hat man wieder das gleiche Muster — egal ob die Bedingung zutrifft oder nicht, es wird in beiden Fällen `row` zu `daten` hinzugefügt, also kann man das heraus ziehen, womit der ``else``-Zweig leer wäre, und man ihn weglassen kann:

Code: Alles auswählen

                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
                    if row[inhalt] != '1':
                        zaehler += 1
                daten.add(tuple(row))
Analog kann man den ``elif``-Zweig mit diesen zwei Schritten vereinfachen. Die Struktur dort ist ja identisch. Dann sind sieht der Code so aus:

Code: Alles auswählen

            if row[ve] == 'VE' and row[inhalt] == '1':
                zahlenwert = regexp.search(row[beschreibung])
                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
                    if row[inhalt] != '1':
                        zaehler += 1
                daten.add(tuple(row))

            elif row[ve] == 'STÜCK':
                zahlenwert = regexp.search(row[beschreibung])
                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
                    if row[inhalt] != '1':
                        row[ve] = 'VE'
                        zaehler += 1
                daten.add(tuple(row))

            else:
                daten.add(tuple(row))
Und auch hier sehen wir wieder, dass egal ob die erste, die zweite, oder keine Bedingung zutrifft, in jedem Zweig am Ende `row` zu `daten` hinzugefügt wird. Also kann man das heraus ziehen:

Code: Alles auswählen

            if row[ve] == 'VE' and row[inhalt] == '1':
                zahlenwert = regexp.search(row[beschreibung])
                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
                    if row[inhalt] != '1':
                        zaehler += 1

            elif row[ve] == 'STÜCK':
                zahlenwert = regexp.search(row[beschreibung])
                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
                    if row[inhalt] != '1':
                        row[ve] = 'VE'
                        zaehler += 1
            
            daten.add(tuple(row))
Hier würde mich jetzt noch ein wenig stören, dass der Inhalt der beiden Zweige nahezu identisch ist. Das könnte man so lösen:

Code: Alles auswählen

        for row in my_reader(infile):
            if (row[ve] == 'VE' and row[inhalt] == '1') or row['ve'] == 'STÜCK':
                zahlenwert = regexp.search(row[beschreibung])
                if zahlenwert:
                    row[inhalt] = (
                        (zahlenwert.group('VALUE') or '1').replace('.', '')
                    )
                    if row[inhalt] != '1':
                        if row['ve'] == 'STÜCK':
                            row[ve] = 'VE'
                        zaehler += 1
            
            daten.add(tuple(row))
In der `get_stueck()` kann man genau so das ``daten.add(tuple(row))`` aus dem ``if``/``else`` heraus ziehen.
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack,
einen rießen Dank an Dich!
Deine ausführliche Erklärung, hat mir die Augen geöffnet und der Knoten ist geplatzt. :wink:

Nach jetziger Sicht, habe ich mir da ziemlich unnötige Arbeit gemacht.
Das Konstrukt sieht jetzt einfacher und übersichtlicher aus.

Ein kleiner Schönheitsfehler ''row['ve']'' muß ''row[ve]'' sein.

Eine Frage habe ich noch.
Bei Deinem letzten Teil, hast Du dies

Code: Alles auswählen

                if zahlenwert:
                    row[inhalt] = (zahlenwert.group('VALUE') or '1').replace('.', '')
zu dem

Code: Alles auswählen

                if zahlenwert:
                    row[inhalt] = (
                        (zahlenwert.group('VALUE') or '1').replace('.', '')
                    )
geändert.
Besteht da ein Unterschied, oder ist das nur eine andere Schreibweise und ob man dies so oder so schreibt ist egal?
Zuletzt geändert von Nobuddy am Freitag 27. April 2012, 16:53, insgesamt 1-mal geändert.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Nobuddy hat geschrieben:Besteht da ein Unterschied, oder ist das nur eine andere Schreibweise und ob man dies so oder so schreibt ist egal?
Innerhalb von Klammern kann man problemlos einen Zeilenumbruch einfügen, der wird dann ignoriert. Auf diese Weise hat BlackJack die Übersichtlichkeit erhöht. Es ist auch dann nützlich, wenn man die lt. PEP-8 vorgeschlagene Maximalbreite von 80 Zeichen je Zeile überschreitet.
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo sparrow,

Danke für Deine Erklärung, das sind gute Argumente!
Antworten