Seite 2 von 2

Re: Problem bei Wertübergabe in Funktion

Verfasst: Donnerstag 26. April 2012, 15:20
von Nobuddy
@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.')

Re: Problem bei Wertübergabe in Funktion

Verfasst: Donnerstag 26. April 2012, 18:10
von 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.

Re: Problem bei Wertübergabe in Funktion

Verfasst: Freitag 27. April 2012, 07:38
von Nobuddy
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.

Re: Problem bei Wertübergabe in Funktion

Verfasst: Freitag 27. April 2012, 09:37
von 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.

Re: Problem bei Wertübergabe in Funktion

Verfasst: Freitag 27. April 2012, 16:23
von Nobuddy
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?

Re: Problem bei Wertübergabe in Funktion

Verfasst: Freitag 27. April 2012, 16:47
von sparrow
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.

Re: Problem bei Wertübergabe in Funktion

Verfasst: Freitag 27. April 2012, 16:54
von Nobuddy
Hallo sparrow,

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