Versuch einer main() Funktion - Basename Error

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • tdl Was die Funktion macht habe ich schon verstanden. Nur sollte sie das halt nicht alles machen und teilweise halt auch nicht so wie sie das macht. Ja es gibt Fragen das kann man trefflich streiten wie man es nun genau löst, und Du fragst 10 Programmierer und bekommst 20 verschiedene Antworten. Aber das eine Funktion *eine* in sich geschlossene Sache macht und so geschrieben sein sollte, dass sie eine sinnvolle Schnittstelle hat was Argumente und Rückgabewerte betrifft, und dass sie leicht testbar sein sollte, ist keine Geschmacksfrage. Das mit dieser Funktion definitiv was nicht stimmt, sieht man wie eingangs erwähnt schon am Namen, weil man da Probleme hat einen guten Namen zu finden der beschreibt was die Funktion tut, ohne das man da einen ganzen Roman als Namen hat.

    Das mit dem ``x in range(a, b)`` wird ziemlich sicher jeder in ``a <= x < b`` ändern. Und wirklich *niemand* würde aus ``a <= x < b`` ein ``x in range(a, b)`` machen. Auch das ist keine Geschmacksfrage. Die Änderung ist a) klein, aber b) ist der Unterschied ob man maximal vier oder maximal 60 Vergleiche macht ziemlich deutlich. Es gibt keinen Grund das nicht in die Form zu bringen die 99,99% aller Python-Programmierer wählen würden (also wenn es da nicht `datetime.time` gäbe), ausser dass man mit dem Fuss aufstampfen und es unbedingt anders machen will als den offensichtlichen Weg zu nehmen. Wie gesagt, gerade bei Python käme so etwas nicht ohne Anmerkung durch einen Code Review. Wenn Du so was machen willst ist Python vielleicht die falsche Sprache, zumindest wenn Du andere Python-Programmierer was fragst, weil solche Sachen werden Dir erfahrungsgemäss immer wieder gesagt, bis keiner mehr Lust hat auf die Fragen zu antworten wenn Du gar nichts lernen willst.

    `Path`-Objekte haben eine `write_text()`-Methode (ungetestet):

    Python
    def log_schreiben(text):
        LOG_DATEI_PFAD.write_text(text, encoding="ascii")
    
    # oder:
    
    from functools import partial
    
    log_schreiben = partial(LOG_DATEI_PFAD.write_text, encoding="ascii")

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • `Path`-Objekte haben eine `write_text()`-Methode (ungetestet):

    Da war Dennis89 leicht schneller. :) Ich hatte nicht weit genug nach unten gescrollt oder es glatt überlesen auf der Seite. Das ist tatsächlich um Längen einfacher. Hab ich übernommen, die Funktion "log_schreiben" gestrichen und stattdessen eine Funktion "weckzeit_löschen" geschrieben.

    Code
    def weckzeit_löschen():
        LOG_DATEI.write_text("Wecker ist aus!")
        print ("\nWeckzeiten werden gelöscht!")
        run([SNIPER], check = True)

    Das mit dem ``x in range(a, b)``

    The advantage of the range type over a regular list or tuple is that a range object will always take the same (small) amount of memory, no matter the size of the range it represents (as it only stores the start, stop and step values, calculating individual items and subranges as needed).

    Ich lese da raus, dass mitnichten "60 Vergleiche" gemacht werden, sondern nur der Start und Stopwert genommen werden. Ich gleiche ja auch nicht ab ob "minute" im Bereich 0-60 enthalten ist sondern ob sie außerhalb dieses Bereichs liegt (if not). Und da scheint range in diesem Fall nichts anderes als eine klein, größer, gleich Abfrage zu machen.

    Nichts desto trotz habe ich auch diese Zeile geändert. ;)


    Das mit dieser Funktion definitiv was nicht stimmt, sieht man wie eingangs erwähnt schon am Namen, weil man da Probleme hat einen guten Namen zu finden der beschreibt was die Funktion tut, ohne das man da einen ganzen Roman als Namen hat.

    Ich hab die Funktion jetzt probeweise WIEDER in aufgeteilt, um dann in der main() diese beiden Fuktionen direkt nacheinander auszuführen.

    Gefällt mir nicht! Werde ich wieder rückgängig machen. Ich bin auch nach wie vor der Meinung, dass du dich da zu sehr am Namen störst.

    Vielleicht sollte ich sie wirklich in "arg-check()", "einganscheck()" oder vielleicht auch "hakan_check()" umbenennen. :lol:

    gegnüber:

    Und wenn du schreibst "..Was die Funktion macht habe ich schon verstanden..." verstehe ich deine Kritik am Namen nicht. "mein_name_tut_nichts_zur_sache()", bei diesem Namen OK, aber nicht bei den vorherigen nur weil du persönlich einen anderen Namen wählen würdest oder meine Namenswahl nicht nachvollziehen kannst.

    Kritik ist angekommen, ich sehe es anders. Wenn du das als "aufstampfen" bezeichnen möchtest, steht dir das frei.

    Auch der Satz: "...bis keiner mehr Lust hat auf die Fragen zu antworten wenn Du gar nichts lernen willst....". Wenn es dich stört, wenn nicht jeder deiner Vorschläge auch umgesetzt wird, tut es mir leid, aber so ist das Leben. Ich hab doch auch vieles von dir angenommen (str bei strftime weg, Path.write_text, exit() entfernt, while True entfernt, am Ende sogar range durch <=> ersetzt, gegenüber "ich lass die Funktion in einem").

    Danke

    für deine Hilfe

    Thorsten

  • tdl Das mit dem `range` kannst Du da nicht draus lesen. Der ``in``-Test (und auch ``not in``) verwenden eine eventuell vorhandene `__contains__()`-Methode. Falls es die nicht gibt, dann iteriert Python einfach über die Elemente und vergleicht jedes. Und falls es die gibt, muss man hoffen, dass die das nicht auch macht, sondern effizienter. Und ganz so einfach mit den beiden Vergleichen kann die das auch nicht machen, weil `range`-Objekte ja auch eine Schrittweite haben. Ja der Defaultwert ist 1, aber muss er ja nicht, das muss man bei der Berechnung also berücksichtigen.

    `range` erbt von `collections.abc.Sequence` und dort sieht die `__contains__()`-Methode so aus:

    Das ist das was die Dokumentation garantiert. Ob `range` das jetzt mit etwas effizienterem überschreibt ist ein Implementierungsdetail.

    Was ist CPython in C bei `range`-Objekten bei ``in`` passiert beschreibt folgender C-Code:

    `range_contains()` prüft ob es sich um eine ganze Zahl oder einen Wahrheitswert handelt, falls nicht, wird jedes Objekt in der `range`-Sequenz erzeugt und verglichen. Wir haben im Programm aber ganze Zahlen, da wird dann `range_contains_long()` ausgeführt. Ja es ist in C und darum etwas schneller als Python-Code, aber es ist auch ein bisschen komplexer als einfach nur zwei Vergleiche.

    Ich habe mich nicht am Funktionsnamen gestört! Der war ja gut, weil er beschreibt was die Funktion macht. Und man konnte halt am Namen schon sehen, dass die Aufteilung des Codes falsch ist. Da jetzt einfach den Namen zu ändern, so dass nur noch ein Teil von dem was die Funktion tatsächlich tut, im Namen steckt, wäre ein schlechter Name. Und Namen sind wichtig! Das ist nicht irgendwie Kosmetik oder etwas was man irgendwann hinterher mal anpassen kann. Weil man an sinnvollen Namen genau solche Probleme ablesen kann. Und wenn kein passender Name gefunden werden kann, ist das auch oft ein Hinweis, dass man etwas falsch macht. Das die Funktion zu viel macht. Oder Teile von mehreren Aufgaben aber nichts so ganz richtig durchzieht. Und bei Datenstrukturen und Datentypen deuten Namensfindungsprobleme in der Regel darauf hin, dass man Sachen bündelt die nicht zusammengehören. Oder dass man das Problem, oder gar die eigene Lösung nicht ganz im Griff hat.

    Die neue Aufteilung auf zwei Funktionen ist auch nicht gut. Die erste Funktion prüft nicht nur etwas sondern *macht* auch was, was man nicht erwartet, und ein Prüfergebnis gibt die auch nicht zurück. Die gibt entweder implizit `None` zurück oder explizit `False`. Das ist extrem schräg und nutzlos. Ich schrob ja schon, dass man bei einer Prüfung `True` oder `False` erwartet, so dass der *Aufrufer* dann basierend auf dem Ergebnis der Prüfung entscheiden kann was (nicht) gemacht werden soll. Das eine Prüffunktion eine Datei löscht ist unerwartet.

    `plausi_check()` hat dann wieder das Problem, dass da `exit()` drin vorkommt. Irgendwelche Funktionen sollten nicht den gesamten Prozess beenden. Das ist schlecht testbar und man kann auch nicht weitere Funktionalität einbauen die *nach* dem Aufruf der Funktion etwas mit dem Ergebnis macht, dass da eigentlich geliefert werden sollte.

    Beide Funktionen haben das Problem das sie auf globalen Werten arbeiten was magisch und undurchsichtig ist. Was da geprüft wird, sollte als Argument übergeben werden, damit man das zum Beispiel auch einfach interaktiv oder automatisiert testen kann, beispielsweise bei der Fehlersuche.

    `main_name_tut_nichts_zur_sache()` sollte `main()` heissen, *dann* wäre die Funktion fast okay. In den ``else``-Zweig würde kein sinnloses ``return True`` gehören, das was passieren soll wenn die Prüfungen vorher erfolgreich durchgelaufen sind.

    ``print(FEHLER)`` und ``exit()`` sollten zu einem ``exit(FEHLER)`` verschmelzen, damit der Fehler auf der Standard-Fehlerausgabe landet und das Programm *nicht* mit einem Rückgabecode von 0 endet — der per Konvention aussagt alles wäre fehlerfrei durchgelaufen.

    Üblicherweise trennt man das parsen, also umwandeln und validieren der Argumente, von den Aufgaben die danach mit den Daten durchgeführt werden. Da gibt es auch Bibliotheken für, die einem da viel Arbeit abnehmen. In der Standardbibliothek beispielsweise `argparse` oder zum Beispiel die externe `click`-Bibliothek.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!