"SwitchCase" Problem

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
!false
User
Beiträge: 3
Registriert: Mittwoch 14. Februar 2018, 20:46

Mittwoch 14. Februar 2018, 21:11

Hi Leute,
bin neu hier im Forum und habe direkt eine Frage an euch :)

Das python anders als java kein richtiges switch case parat hat, habe ich mitbekommen und mich um eine alternative implementierung bemüht.
Das dieses Konstrukt sicher auch mit einem if, elif, else Konstrukt zu realisieren ist, ist mir bewusst. Der Übersicht , auch wenn das debuggen nicht so nett sein soll, mächte ich doch auf ein analoges Konstrukt zum SC zurückgreifen.

Ich habe folgende Problemstellung.
Ich lesen Daten aus einer Datei welche in einem String gespeichert werden. Der String soll ad hoc über ein Switch Case (ich nenne es einfach so auch wenn es kein richtiges ist) idetifiziert werden, enthält der String z.B. 'Ueberschrift' wird ein bestimmter Case aufgerufen.
Dies ganze ist absolut statisch, die ausgelesnenen Daten enthalten immer die selben Inhalte. Ich benötige aber nicht immer alle, es soll also beim durchjagen identifiziert werden was ich gerade auslese.

Ich habe nun an folgendes gedacht

Code: Alles auswählen

class Auslesen():

 def switchContent(rootContent, childContent):
     auslesen = Auslesen()
     tempChildContent = childContent
     switcher = {
         'Ueberschrift: auslesen.ueberschrift(rootContent tempChildContent)
    }
    return switcher.get(rootContent, "nothing")
Es soll in Prinzip ein String Vergleich erfolgen, ist der gerade eingelesene String im Switch Case vorhanden soll eine bestimmte Methode aufgerufen werden, ist er nicht enthaltensoll im Prinzip der default Wert geworfen werden.

Mein Problem ist folgendes, selbst wenn z.B 'Kopfzeile' eingelese wird, reagiert er auf den Case 'Ueberschrift'.
Die Methode wird aus einem anderen Modul aus aufgerufen und der String wird als Argument übergeben.

Hoffe jemand kann mir helfen :)

Gruß !false
__deets__
User
Beiträge: 2850
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 14. Februar 2018, 21:37

Was du da an Code gezeigt hast finde ich sehr verwirrend. Aber eine Methode das zu machen ohne auf if-Kaskaden zurueck zu greifen ist ein Woerterbuch:

Code: Alles auswählen

{
    "fall_a": funktion_fuer_fall_a,
     "fall_b": funktion_fuer_fall_b,
}.get(schluessel, lambda _: None)(argument_an_funktion)
Das lambda ist einfach nur ein Trick, um im Fall einer nicht-Uebereinstimmung einfach nix zu machen. Das Argument habe ich mir zu Dekorationszwecken ausgedacht, geht natuerlich auch ohne, die gleichen Argumente muessen es natuerlich fuer alle Funktionen sein.

Nachtrag: jetzt verstehe ich glaube ich, warum ich verwirrt war: dir fehlte ein schliessendes ', und dadurch war das Syntax-Highlighting komplett weg.

Wie du an meinem Beispiel siehst, bist du schon nah dran. Du kanns nur die Funktionen nicht schon bei Definition des Woerterbuches mit Parametern aufrufen, sondern musst sie *nach* dem Lookup reinreichen. Wie bei mir gezeigt.
!false
User
Beiträge: 3
Registriert: Mittwoch 14. Februar 2018, 20:46

Mittwoch 14. Februar 2018, 22:21

Sry , da habe ich ein ' nach der Ueberschrift vergessen :)
Ah verstehe. Habe dieses Konstrukt "....}.get(schluessel, lambda _: None)(argument_an_funktion)" welches du gezeigt hast in einem Beispiel gesehn, allerdings so " ....}.get(schluessel, lambda _: None)()" ich wusste nicht das nach dem Lookup argumente übergeben werden und wozu die öffnende und schließende Klammer am Ende sind.
Komisch ist es, dass der Aufruf der Funktion mit Argumenten klappt, allerdings wird der Vergleich halt nicht beachtet, sondern immer durchgegeben.
Danke dir für die schnell Hilfe, werde es Morgen direkt ausprobieren :) hoffe erfolgreich!

Melde mich ob es geklappt hat!
__deets__
User
Beiträge: 2850
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 14. Februar 2018, 22:26

So wie du das gerade zitiert hast kannst du das nicht gesehen haben bzw funktioniert es nicht. Dieses Muster klappt nur, wenn alle Funktionen die gleich Anzahl Argumente bekommen (0 ist auch eine Anzahl), und das lambda die ebenfalls hat. Deines hat ein Argument (den unterstrich), aber der Aufruf übergibt keins -> Krach.

Deine anderen Ausführungen verstehe ich nicht. In deinem Beispiel wird natürlich IMMER die Funktion aufgerufen, schon beim Aufbau des Wörterbuches. Falls du das meinst.
Benutzeravatar
snafu
User
Beiträge: 5449
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 15. Februar 2018, 11:33

Ich würde auch ein Dict nehmen, aber es wohl etwas ausführlicher machen:

Code: Alles auswählen

def call(func_name, *args, **kwargs):
    funcs = {
        "foo": func1,
        "bar": func2,
    }
    func = funcs[func_name]
    return func(*args, **kwargs)
Dies wirft einen KeyError bei einem unbekannten Namen, anstatt None zu liefern. So lässt sich gut unterschieden zwischen Funktionen, die tatsächlich None liefern und welchen, die nicht existieren.
shcol (Repo | Doc | PyPi)
Benutzeravatar
snafu
User
Beiträge: 5449
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 15. Februar 2018, 13:49

Hier noch etwas ausgebaut:

Code: Alles auswählen

from functools import partial as call
from random import choice

def switch(value, cases):
    case = cases.get(value, cases.get('default'))
    if not case:
        raise ValueError('Unknown case')
    return case()

def print_python(method):
    py = 'python'
    return switch(method, {
        'upper':
            call(print, py.upper()),
        'lower':
            call(print, py.lower()),
        'title':
            call(print, py.title()),
        'default':
            call(print, py)
    })

def main():
    methods = ['upper', 'lower', 'title', 'spam']
    for _ in range(10):
        print_python(choice(methods))

if __name__ == '__main__':
    main()
Wenn man bei dem Beispiel den default-Fall auskommentiert, dann wirft er eine Fehlermeldung beim undefinierten Fall.

EDIT:
Ich gebe zu, das Beispiel ist etwas doof, weil das zweite Argument von call() vorab ausgeführt wird. Genau sowas möchte man ja meistens vermeiden. Aber ich hoffe, man versteht die Idee dahinter.
shcol (Repo | Doc | PyPi)
__deets__
User
Beiträge: 2850
Registriert: Mittwoch 14. Oktober 2015, 14:29

Donnerstag 15. Februar 2018, 14:07

@snafu: das ist ziemlich verwirrend. Warum benennst du denn partial um in call? So erweckst du den Eindruck, das callable schon im dict aufzurufen. Und der Gewinn, ein default-key einzufuehren statt wie bei mir eine default-*Funktion* zu nehmen, erschliesst sich mir auch nicht. Ploetzlich hast du ein magisches Wort aus der Domaene eingefuehrt, das man ja nicht benutzen darf. Dafuer benutzt man sonst None.

In meinen Augen sehr unklar und insgesamt leider nicht empfehlenswert.
Benutzeravatar
snafu
User
Beiträge: 5449
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 15. Februar 2018, 14:29

War mir klar, dass sowas kommt. Es ist halt näher am switch aus anderen Sprachen. Wer Python gewöhnt ist, will das wahrscheinlich nicht benutzen. Und ich wollte an sich auch nur zeigen, dass es in leicht abgewandelter Form auch mit Python-Mitteln möglich ist. Ob man das tatsächlich so benutzen will, kann ja jeder selbst entscheiden.
shcol (Repo | Doc | PyPi)
__deets__
User
Beiträge: 2850
Registriert: Mittwoch 14. Oktober 2015, 14:29

Donnerstag 15. Februar 2018, 14:36

Fortran kann man in jeder Sprache programmieren...

Es ist nahe dran, wenn man switch nur mit einem Statement wuenscht, und ohne fall-through und ... fuer irgendeine Definition von nahe dran, ja. Pythonisch ist's nicht. Da waere die if-Kaskade klarer.
Sirius3
User
Beiträge: 7779
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 15. Februar 2018, 15:21

@snafu: die grobsten Schwächen man korrigiert, indem man statt partial lambda nimmt und default als "Keyword" definiert:

Code: Alles auswählen

from random import choice
 
DEFAULT = object()

def switch(value, cases):
    case = cases.get(value, None) or cases.get(DEFAULT)
    if case:
        return case()
 
def print_python(method):
    py = 'python'
    return switch(method, {
        'upper':
            lambda: print(py.upper()),
        'lower':
            lambda: print(py.lower()),
        'title':
            lambda: print(py.title()),
        DEFAULT:
            lambda: print(py),
    })
 
def main():
    methods = ['upper', 'lower', 'title', 'spam']
    for _ in range(10):
        print_python(choice(methods))
 
if __name__ == '__main__':
    main()
Wirklich schön wird es dadurch aber auch nicht.
!false
User
Beiträge: 3
Registriert: Mittwoch 14. Februar 2018, 20:46

Sonntag 18. Februar 2018, 23:08

@__deets__
vielen Dank für die Hilfe, die erste Implementierung von dir hat wunderbar geklappt.
Allerdings bin ich nun doch vom SwitchCase weg, da ich knapp 20 Fäll habe die ich abfangen muss. Nicht bei jedem Fall werden die selben Argumente benötigt, ist mir erst bei der Implementierung Bewusst geworden. Bin ich zu den if, elif.. übergegangen, wobei ich das recht unschön finde :K

@all vielen Danke für die anregenden Beiträge :)
Antworten