Posts by __blackjack__

    Von `call()` wird in der Dokumentation abgeraten und auf `run()` verwiesen.


    Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Besonders unübersichtlich ist es wenn man Hauptprogramm und Funktionsdefinitionen vermischt.


    Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).


    Konstanten werden am Anfang des Programms definiert, nach den Importen. Damit man die leicht finden und anpassen kann.


    `myfunc_` ist ein überflüssiger, nichtssagender Präfix, der da nichts zu suchen hat. Funktionen (und Methoden) werden nach der Tätigkeit benannt, die sie durchführen. Damit der Leser weiss was passiert, und damit man Funktionen von eher passiven Werten unterscheiden kann. `abstand` und `distanz` sind da beispielsweise sehr verwirrend. Beides bedeutet im Grunde das gleiche, aber das eine ist eine Funktion die das andere berechnet. Man kann aber nicht am Namen erkennen was der Wert ist und was die Tätigkeit die zu dem Wert führt.


    Einen lokalen Namen zu haben der gleichlautend mit dem Funktionsnamen ist, verwirrt unter Umständen auch ein bisschen.


    Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.


    `GPIO.cleanup()` sollte nicht nur bei Strg+C augerufen werden, sondern *immer* wenn das Programm verlassen wird. Auch wenn es eine Ausnahme oder ein anderes Signal war. Das gehört also in einen ``finally:``-Zweig.


    `time.time()` ist für Zeitmessungen nicht geeignet, da nicht garantiert ist, dass diese Zeit immer ”vorwärts” läuft. Dafür gibt es `time.monotonic()` oder falls man die höchste Auflösung braucht, die das System zur verfügung stellt: `time.perfcounter()`.


    Für Wahrheitswerte gibt es in Python einen eigenen Datentyp, da sollte man nicht 0 und 1 für missbrauchen. Dann weiss der Leser auch gleich das es nur zwei Monitorzustände gibt. Die Variable würde ich auch immer erst *nach* der Funktion setzen, die den Monitor schaltet, denn nur dort kann man (halbwegs) sicher sein, dass das auch stimmt.


    Die ``if``/``elif``-Struktur im Hauptprogramm testet in den Bedingungen Sachen doppelt und teilweise explizit das Gegenteil von vorherigen Bedingungen. Das ist unübersichtlicher als es sein müsste, und auf fehleranfälliger bei Änderungen, weil man immer daran denken muss auch die gegenteiligen Bedingungen anzupassen, wenn man etwas ändert. An der Stelle kann man dann schon fragen ob die eine 100, statt 120 im Programm schon so ein Fehler ist, oder das tatsächlich so gewollt ist.


    Ungetestet:

    neuling10 Mach mal ein df -h wenn das NAS an ist (und auf dem Raspi eingebunden) und dann noch mal wenn das NAS heruntergefahren ist. Ich vermute mal *das* hängt dann wenn der Pi denkt er sollte das eigentlich noch erreichen können.


    Das liesse sich sehr einfach fixen, allerdings verrate ich den Fix nicht, weil ich df aufrufen wie schon gesagt doof finde. Das kann man auch mit Python lösen, ohne externes Programm.


    Der ``df``-Aufruf hat sowieso noch einen Fehler. Der wird zwar nicht so wirklich relevant sein, aber die Formatierung der Zahlen geht einfach davon aus, dass schon jeder Wert in der Einheit Gigabyte sein wird und ignoriert die Einheitsangaben von ``df`` komplett. Sauber ist das nicht.

    Das ist wirklich eine unberechtigte Angst, dass ohne Typprüfung doch dauernd solche Sachen passieren müssten. Und es ist selten, dass so etwas bis in den Produktionseinsatz durchrutscht. Selbst ohne volle Unit-Test Abdeckung probiert man doch Code den man geschrieben hat, doch wenigstens manuell mal aus. Auch bei statisch typisierten Programmiersprachen, denn der Compiler findet ja keine Logikfehler. Automatisierte Tests sind in Python (und anderen dynamischen Programmiersprachen) oft auch wesentlich leichter geschrieben. Das ”pythonische” Rahmenwerk dafür ist `pytest`. Mock-Objekte sind durch die dynamische Natur der Sprache einfach zu schreiben, beziehungsweise braucht man oft gar keine schreiben, sondern kann generische verwenden. Die Standardbibliothek hat da ein Modul für (`unittest.mock`).

    Die Aussagen das Programm sei fertig und Du weisst nicht wie Du die Sensoren auslesen sollst, schliessen sich ja irgendwie gegenseitig aus. 😉


    Was hast Du denn schon gemacht um herauszufinden wie man die jeweiligen Sensoren ansprechen kann? Liste doch mal wie man die jeweils anschliesst, welche Protokolle die ”sprechen” und welche C-Bibliotheken es dafür jeweils gibt. Oder wolltest Du jetzt einfach die komplette Recherche und vielleicht sogar noch die Programmierung hier anderen überlassen? 😀

    Wenn das in Production-Code auftaucht, dann hat man eventuell nicht genug Unit-Tests beziehungsweise keine ausreichende Testabdeckung. Das kommt in der Praxis nicht so wirklich oft vor.

    Man sollte einfach gar nicht diese gruselige funktionsbasierte Schnittstelle verwenden die auf globalem Zustand basiert, und man eben auch mit den üblichen Problemen rechnen muss die durch globalen Zustand entstehen können. Diese Schnittstelle ist hauptsächlich da weil Benutzer diesen Rotz von älteren, anderen Programmen zur Erstellung von Plots gewohnt sind, und das geringfügig praktischer ist, wenn man das interaktiv in einer Python-Shell verwendet.


    In `csv_auswerten()` ist ein unnötiges ``pass``.


    In die Funktion sollte kein leeres Wörterbuch übergeben werden um das zu füllen. Die Funktion sollte das selbst erstellen und dem Aufrufer dann das ausgefüllte Wörterbuch als Rückgabewert liefern.


    `bericht` verkompliziert den Code für jeden verarbeiteten Datensatz unnötig. Statt entweder das Flag oder den Datensatz zu prüfen ob der relevante Bereich angefangen hat, würde man das besser *vor* der Schleife klären, welche dann die Datensätze verarbeitet und sich nicht darum kümmern braucht ob noch nach dem Anfang gesucht wird. Kriterium ist auch nicht ob "Runde" *irgendwo* im Datensatz vorkommt, sondern ob es das erste Feld ist. Sonst fällt das auf die Nase wenn sich jemand "Runde" nennt und das deshalb auch in den Zeilen vor den Rundendaten in einem Feld steht.


    Literale Zeichenketten sind keine Kommentare. Kommentare sind ausschliesslich durch ``#`` gekennzeichnet.


    Folgender Code ist zu umständlich:

    Python
                if row[3] in teilnehmer and row[7] in teilnehmer:
                    schaden_addieren(row, teilnehmer)
                else:
                    if not row[3] in teilnehmer and not row[3] == "--":
                        teilnehmer.update({row[3]: Statistik()})
                    if not row[7] in teilnehmer and not row[7] == "--":
                        teilnehmer.update({row[7]: Statistik()})
                    schaden_addieren(row, teilnehmer)

    Als erstes gehört gleicher Code der in allen Zweigen am Anfang oder Ende steht, *einmal* vor beziehungsweise hinter das Konstrukt:

    Python
                if row[3] in teilnehmer and row[7] in teilnehmer:
                    pass
                else:
                    if not row[3] in teilnehmer and not row[3] == "--":
                        teilnehmer.update({row[3]: Statistik()})
                    if not row[7] in teilnehmer and not row[7] == "--":
                        teilnehmer.update({row[7]: Statistik()})
                
                schaden_addieren(row, teilnehmer)

    Jetzt macht der ``if``-Zweig aber gar nichts mehr. Und im ``else``-Zweig wird ist das *Gegenteil* von der Bedingung im jetzt leeren ``if``-Zeig. Also ist das ”äussere” ``if``/``else`` komplett überflüssig:

    Python
                if not row[3] in teilnehmer and not row[3] == "--":
                    teilnehmer.update({row[3]: Statistik()})
                if not row[7] in teilnehmer and not row[7] == "--":
                    teilnehmer.update({row[7]: Statistik()})
                
                schaden_addieren(row, teilnehmer)

    Die ``not`` stehen an der falschen Stelle oder sind überflüssig und `update()` ist der falsche Weg um *einem* Schlüssel einen Wert zuzuordnen:

    Python
                if row[3] not in teilnehmer and row[3] != "--":
                    teilnehmer[row[3]] = Statistik()
                if row[7] not in teilnehmer and row[7] != "--":
                    teilnehmer[row[7]] = Statistik()
    
                schaden_addieren(row, teilnehmer)

    Im Grunde steht da aber nun zweimal das gleiche, nur der Index ist unterschiedlich:

    Python
                for name in (row[index] for index in [3, 7]):
                    if name not in teilnehmer and name != "--":
                        teilnehmer[name] = Statistik()
    
                schaden_addieren(row, teilnehmer)

    Letztlich ist das aber alles umständlicher als einfach ein `collections.defaultdict` zu verwenden.


    In `schaden_addieren()` ist viel ähnlicher Code den man durch eine Schleife über die Unterschiede ersetzen kann.


    `addiere_schaden()` ist etwas umständlich wenn der `typ` immer der Attributname ist. Da kann man doch einfach `getattr()` verwenden. Damit wird die Methode zu einem Einzeiler.


    Zwischenstand:

    Das mit den `MAX_RUNDEN` ist komisch gelöst. Letztlich sieht das auch so aus als wäre das mit Pandas und vielleicht `seaborn` einfacher zu lösen die Daten zu filtern, gruppern, aggregieren.

    framp Naja, indirekt schon weil Duck Typing und das Ausmass an ”Dynamik” die Python erlaubt, dem Compiler eine ganze Menge Möglichkeiten der Optimierung nimmt. Die Grenze erreicht da so etwas wie PyPy, das obwohl dort ein JIT-Compiler nativen Maschinencode zur Laufzeit erstellt, trotzdem noch bei jedem Aufruf getestet werden muss, ob die Typen zum erstellten Maschinencode passen, selbst wenn sie das immer tun, weil für potentiell andere Typen dann wieder der JIT-Compiler laufen muss. Das ist bei Java anders, weil dort der JIT-Compiler einmal Maschinencode für die durch die statische Typisierung bekannten Typen erzeugen kann, und bei jedem Aufruf dann einfach diesen einen Code ausführen kann, weil die Typen immer gleich sein werden.


    Und das kann man auch als Unterschied zwischen late und early binding sehen. Bei C++ und Java sind die Typen in Signaturen zur Übersetzungszeit bekannt. Bei Python ist letztlich jedes Argument *garantiert* nur von `object` abgeleitet, aber man weiss nicht einmal ob die Funktion oder Methode zur Laufzeit nicht noch ersetzt wird, also nicht nicht einmal die tatsächliche Signatur von Aufrufen nachdem der Quelltext in Bytecode übersetzt wurde.

    Michdo93: Die Kompilierungsgeschwindigkeit ist relativ egal und ich habe auch den Eindruck Du verwechselst so ein bisschen Laufgeschwindigkeit und Kompilierungsgeschwindigkeit; und kompilieren in nativen Maschinencode und statische Typisierung. Was die Plattformunabhängigkeit angeht dürften sich hier C++, Java, und Python nichts nehmen. JVM bedeutet nicht, das es nicht auch einen JIT compiler geben kann, und einiges dann am Ende nicht doch in nativem Maschinencode läuft. So etwas gibt es mit PyPy auch für Python.


    Ich denke die Frage ob Python oder C++ ist in den meisten Fällen weniger eine Frage der Ausführungsgeschwindigkeit, sondern der Geschwindigkeit in der man Programme entwickeln kann. Denn Python setzt zum Ansprechen der GPIOs am Ende des Tages ja auch auf wiringPi oder pigpio und es gibt viele Bibliotheken die unter der Haube C, C++, Fortran, … benutzen, an den Stellen wo es auf rohe Rechenleistung ankommt. Diese Strategie kann man auch in eigenen Python-Programmen verfolgen. Falls es Leistungsprobleme gibt, messen wo die liegen, und dann den Falschenhals in Cython oder einer anderen Programmiersprache beseitigen, und in das Python-Programm einbinden.

    Sagen es gäbe genug Statistiken und dann aber keine suchen und angeben wollen ist kein Beleg, sondern der Versuch sich darum zu drücken.


    Das Betrugsfälle im eCommerce zugenommen haben ist ja auch gar nicht die strittige Frage, sondern ob die übermässig proportional zugenommen haben, denn der eCommerce-Bereich selbst ist ja massiv gewachsen. Damit gibt es dann natürlich auch mehr Betrugsfälle, weil das natürlich mitwächst.


    Wie schlimm das Problem bei E-Bay ist, da habe ich problemlos Informationen von 2010 gefunden. Dass das fast nur noch aus Abzockern besteht, sowohl auf Käufer- als auch auf Verkäuferseite, und das E-Bay und Paypal das auch völlig egal ist, solange sie ihre Prozente von den Transaktionen kassieren können. 12 Jahre her. Also so grundsätzlich zu behaupten früher war das alles toll und heute wird das immer schlimmer, ist ohne konkrete Zahlen um das zu belegen, sinnlos, weil das nichts darüber aussagt was das konkret bedeutet. Eben ein/Dein persönliches Gefühl.


    Du kannst mit Logik Theorien aufstellen, aber ohne Statistiken/Belege sind das halt keine Fakten, und rein mit Theorien, insbesondere wenn es auch andere logisch nachvollziebare Theorien geben kann, sind auch logisch nachvollziehbare Theorien erst einmal nicht automatisch wahr. Zum Beispiel kann man auch die Argumentation aufstellen, dass die Betrüger gar nicht besser werden müssen, sondern vielleicht sogar leichteres Spiel haben. Dadurch das jetzt mehr Leute mehr Geld im Supermarkt ausgeben müssen und schauen wo sie sparen können, und sich damit auch woanders als den üblichen, vertrauenswürdigeren Quellen umschauen, und so den Betrügern geradezu zulaufen. Und dann auch gerne Preise glauben, die eigentlich zu schön sind um wahr zu sein. Denn warum kauft man etwas bei E-Bay, was man auch bei anderen Quellen, die wesentlich sicherer sind, bekommen könnte.


    Ich käme beispielsweise nie auf die Idee teuren Schmuck bei E-Bay zu kaufen. Also auch vor 10 Jahren schon nicht. Das ist doch viel zu gefährlich.

    Gefühlt wird immer mehr betrogen.

    Gefühlte Fakten also.


    Wobei es natürlich sehr gut sein kann, dass wegen Corona auf E-Bay mehr Betrüger unterwegs sind, weil die, die sonst ”live” unterwegs waren, keinen ”Kundenkontakt” mehr haben konnten. Genau wie sich insgesamt mehr Geschäfte in den virtuellen Raum verlagert haben. Die Frage ist dann ob die Zunahme an Betrügen im Verhältnis zu reellen Geschäftsvorgängen tatsächlich grösser war/ist. Und da kommt man mit „Gefühl“ nicht weiter, da bräuchte man Zahlen zum argumentieren.

    DeaD_EyE Och bitte nicht schon wieder Schwurbel-Kram. Es wird nicht ”mittlerweile” überall betrogen, das ist ein uraltes Phänomen und liegt nicht am ”System”, sondern an Menschen an und für sich. Am Wochenende eine Doku gesehen in der schon die Wikinger Narwal-Stosszähne in Europa als Einhorn-Elfenbein vertickt haben. 🦄

    `rc.local` startet das Programm genau einmal, das startet das nicht während des Betriebs neu. Diese Vermutung ist also schon mal falsch.


    Man müsste in dem Programm mindestens an strategisch wichtigen Stellen Logging-Ausgaben machen um zu sehen wo genau es hängen bleibt. Das geht zum Beispiel mit dem `logging`-Modul aus der Standardbibliothek, oder dem externen `loguru`-Modul. Oder man schiesst mit dem `snoop`-Package auf diesen Spatz und lässt sich wirklich jede ausgeführte Zeile, zumindest in der Schleife, inklusive Werten protokollieren.


    Ansonsten ist das Programm eher gruselig weil das offenbar nicht von einem Python-Programmierer geschrieben wurde. Python kann Texte/Zeichenketten bearbeiten, und auch die Funktionalität einiger der externen Programme die da aufgerufen werden kann man in Python ausdrücken. Dazu muss man keine externe Shell starten in der dann externe Programme und ``cut`` oder ``awk`` zum zerlegen von Zeichenketten und zum Rechnen verwendet werden.


    Da gehören ganz viele Kommentare nicht rein, insbesondere so viel auskommentierter Code sollte da nicht drin stehen.


    Persönliche Anmerkungen: ”Normale” Vektorschriftarten sehen auf so kleinen Displays ja eher bescheiden aus. Ich würde da ja eher spezielle Vektorschriftarten verwenden die Pixelschriftarten ”nachahmen”. Die sehen dann zwar nur in genau der Grösse für die sie vorgesehen sind, gut aus, aber dafür dass dann auch auf so kleinen Displays.


    Zum Beispiel die ”dünnen” 8x8 oder 6x8 Schriftarten von Compaq oder HP-Geräten von https://int10h.org/oldschool-pc-fonts/ Falls der Zeichenvorrat von CP437 ausreicht, gibt es da noch mehr Auswahl.


    Oder Beispielsweise der „Berkelium (BSW) GEOS System Font“ von https://www.kreativekorp.com/software/fonts/c64/

    gwaag: Schon wieder kopierter und leicht angepasster Code. 🙄


    Und er ist fehlerhaft, denn der jeweilige Name wird ja nur definiert falls die innere ``if``-Bedingung in der Schleife wahr ist. Falls nicht, endet das wieder in der Ausnahme weil ein Name nicht definiert ist der in einer Rechnung verwendet wird.


    Ausgehend davon, dass es jeweils nur einen Raum mit dem gleichen Namen gibt, sollte man die Schleife auch abbrechen wenn man den gefunden hat, und nicht sinnlos weitersuchen. Was sich bei einer Funktion mehr oder weniger ”natürlich” ergibt, wenn man an der Stelle wo man den Wert hat, ihn sofort zurückgibt — was die Funktion und damit die Schleife beendet.


    Ungetestet:

    Falls das mehr Temperaturen werden, die man aus den Raumdaten holt, würde es sich vielleicht auch anbieten eine Datenstruktur aus dem Raumdaten zu erstellen wo man das einfacher Abfragen kann. Also beispielsweise ein Wörterbuch das Raumnamen auf Temperaturwerte abbildet.


    Alternativ mit einem Generatorausdruck und `next()` — da bekommt man eine Ausnahme schon mitgeliefert falls kein Raum mit dem Namen gefunden werden kann:

    Python
    def get_temperature(rooms, room_name):
        return next(
            room["sensoren"][1]["wert"]
            for room in rooms
            if room["name"] == room_name
        )

    Oder mit `more_itertools.one()` (externes Modul) statt `next()` — da bekommt man auch eine Ausnahme falls es mehr als einen Raum mit dem gleichen Namen gibt:

    Python
    from more_itertools import one
    
    
    def get_temperature(rooms, room_name):
        return one(
            room["sensoren"][1]["wert"]
            for room in rooms
            if room["name"] == room_name
        )