Vereinfachung immerwiederkehrender Ausdruck

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.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

In welchem Zusammenhang brauchst du solch ein verhalten?
Irgendwie hab ich das Gefühl, dass man das ganze anders gestalten könnte.

Gruss
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

rayo hat geschrieben:In welchem Zusammenhang brauchst du solch ein verhalten?
Ich parse eine bestimmte Dateistruktur. Ähnlich zu INI-Dateien gibt es darin Sektionen, Schlüssel und Werte, also z.B. sowas hier:

Code: Alles auswählen

[Foo]
spam=eggs
Kennen viele von der Samba- oder PHP-Config… Nicht dass mir jetzt jemand mit dem ConfigParser ankommt :D es ist keine Config-Datei und daher lässt sie sich damit auch nicht parsen.

Ich gehe also jede Zeile der Datei durch und prüfe deren Aufbau mittels RegExe. Beispiel:

Code: Alles auswählen

reSection = re.compile(r"^\[([^\]]*)\]$")
reKeyValue = re.compile(r"^([^\=]+)\=(.*)$")

def is_section(line):
    m = reSection.search(line)
    if m:
        # return section name
        return m.group(1)
    else:
        return False

def is_key_value(line):
    m = reKeyValue.search(line)
    if m:
        # return key and value as a tuple
        return m.groups()
    else:
        return False
Gut soweit. Neben Sections und Key/Value-Paaren gibt es noch eine Menge von Sonderformen. Es werden also mehr als zwei Prüffunktionen auf die Zeile angesetzt, aber für's erste soll das reichen. Nun frage ich also jede Zeile ab:

Code: Alles auswählen

for line in lines:
    # check if the line is a section
    section = is_section(line)
    if section:
        # create a new section object and submit the name
        so = SectionObject(section)
        # register the section at the parser
        myParser.register_section(so)
    else:
        # check if the line is a key/value pair
        keyval = is_key_value(line)
        if keyval and s:
            # parse key only if a section object has already been created
            key, value = keyval
            # create a new key object and submit the value if any
            ko = KeyObject(key)
            if value:
                ko.value = value
            # append the key object to the last section
            s.add_key(ko)
        else:
            # more options later...
            more = is_more(line)
            # ...
            else:
                # this line is not interesting, so ignore it
                pass
Schon bei drei Prüffunktionen wird die Verschachtelung recht unübersichtlich, weil ich nicht gleich in einem `elif` den Rückgabewert der Funktion auffangen kann, um damit weiter zu arbeiten. Daher muss ich immer erst ein `else` dazwischenklemmen… jedenfalls sehr unkomfortabel.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Also ich würde Funktionen schreiben (z.B. handleSection, ...) und eine Liste mit [(is_section, handleSection), ...] machen und mit einer schleife durchgehen.

Da du dass aber nicht möchtest würde ich dir vorher noch continue nahelegen, damit gibts keine verschachtelungen

Code: Alles auswählen

for line in lines:
    # check if the line is a section
    section = is_section(line)
    if section:
        # create a new section object and submit the name
        so = SectionObject(section)
        # register the section at the parser
        myParser.register_section(so)
        continue

    keyval = is_key_value(line)
    if keyval and s:
        # parse key only if a section object has already been created
        key, value = keyval
        # create a new key object and submit the value if any
        ko = KeyObject(key)
        if value:
            ko.value = value
        # append the key object to the last section
        s.add_key(ko)
        continue

    # more options later...
    more = is_more(line)
    if more:
        continue
    
    #code ohne match
Gruss
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das Folgende ist zum Beispiel eine recht allgemeingültige Lösung für das Problem. Über die Verwendug von "break" darf sich natürlich ausgibig ausgelassen werden.

Code: Alles auswählen

class IniComponent:
    @staticmethod
    def test(expr)
        assert 0, "implement me!"

    def register(self, parser):
        assert 0, "implement me!"

class Section:
    @staticmethod
    def test(expr):
        #test here
        return ...

    def __init__(self, line):
        #...

    def register(self, parser):
        parser.register_section(self)

class KeyValue:
    @staticmethod
    def test(expr):
        #test here
        return ...

    def __init__(self, line):
        #...

    def register(self, parser):
        parser.register_key_value(self)

for line in lines:
    for i in [Section, KeyValue]:
        if i.test(line):
            i(line).register(parser)
            break
    else:
        print "invalid component"
EDIT: Ach ja, die "return ..." geben natürlich "True" oder "False" zurück. Alternativ könnte man auch gleich eine Instanz erzeugen und registrieren, sieht dann aber irgendwie seltsam aus.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Oha, da gibt's gleich noch ein paar Fragen zum Code :D
EyDu hat geschrieben:

Code: Alles auswählen

class IniComponent:
    @staticmethod
    def test(expr)
        assert 0, "implement me!"
Was macht `@staticmethod` und was bewirkt die Zeile mit `assert`?

Insgesamt aber auch keine schlechte Idee, gefällt mir! Ich lass mir das mal durch den Kopf gehen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

droptix hat geschrieben: Was macht `@staticmethod`
Eine Methode die mit "staticmethod" gekennzeichnet ist, kann ohne eine konkrete Instanz der Klasse aufgerufen werden. Da sollten sich genug Informationen in Texten über OOP finden lassen. Sonst gibt es hier auch noch eine kurze Beschreibung (unter "staticmethod"): HIER.
droptix hat geschrieben: und was bewirkt die Zeile mit `assert`?
Schau dir das mal in der Doku an. Hier wird es dafür verwendet, um zu prüfen ob die methoden auch tatsächlich in den geerbten Klassen implementiert wurden.
droptix hat geschrieben: Insgesamt aber auch keine schlechte Idee, gefällt mir! Ich lass mir das mal durch den Kopf gehen.
Naja, ist ja nicht meine Idee :D In der OOP ein gängiger Weg.

EDIT: Das böse Worte "eigentlich" entfernt :D
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

EyDu hat geschrieben:

Code: Alles auswählen

class IniComponent:
    @staticmethod
    def test(expr)
        assert 0, "implement me!"

    def register(self, parser):
        assert 0, "implement me!"
...
1. Hier wäre eher ein raisen der ``NotImplementedError`` Exception angebracht.

Code: Alles auswählen

@staticmethod
    def test(expr)
        raise NotImplementedError

    def register(self, parser):
        raise NotImplementedError
Ein text wie "implement me!" ist auch eigentlich nicht notwendig, da an dem Typ der Exception schon zu erkenne ist worum es sich handelt.

2. ``assert``s werden zur zusicherung benutzt. Damit teste man Sachen die unmöglich passieren können/dürfen. Falls doch eine ``assert`` Bedingung ``False`` zurückgibt, dann kann von einem Programmfehler (Programmierfehler) ausgegangen werden.

Sehr guter Thread zu dem Thema: http://www.python-forum.de/post-53814.html#53814

IMHO würde ich daher eine nicht implementierte Funktion/Methode, nicht in diese Kategorie werfen und daher lieber die standard Exception ``NotImplementedError`` für solch eine Situation auslösen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

sape hat geschrieben: 1. Hier wäre eher ein raisen der ``NotImplementedError`` Exception angebracht.
Stimmt, da hatte ich nicht dran gedacht.
sape hat geschrieben: 2. ``assert``s werden zur zusicherung benutzt. Damit teste man Sachen die unmöglich passieren können/dürfen. Falls doch eine ``assert`` Bedingung ``False`` zurückgibt, dann kann von einem Programmfehler (Programmierfehler) ausgegangen werden.
Und das ist es ja auch. Aber da es eine entsprechene Exception gibt, sollte man diese auch nutzen.
Antworten