root.vars und globals

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Meine Frage ist prinzipieller Natur.

    Ich weiss, globals zu verwenden sollte vermieden werden.

    Nun kommt man aber doch nicht ganz darum herum Variablen dem ganzen Programm zur Verfügung zu stellen.

    Soweit ich weiss habe ich dazu zwei Möglichkeiten:

    entweder

    global varxy

    varxy = 'xy'

    oder

    self.varxy = 'xy'

    Was ist die besser Variante oder gibt es eine Dritte die ich nicht kenne.

    Danke

    Christoph

  • Hallo,


    jede Funktion die einen Wert benötigt, bekommt diesen als Argument übergeben. Wenn man Namen durch, übertrieben gesagt, 10 Funktionen reichen muss, weil man sie in der 11. benötigt, dann kann es Sinn machen, eine Klasse zu schreiben. Das wäre dann der Teil mit `self`, wobei das keine globale Variable ist, da sie dadurch nicht im ganzen Programm verfügbar ist.


    Vielleicht hast du mal etwas mehr Kontext, dann kann man dir eine Richtung vorschlagen.


    Grüße

    Dennis

    🎧 The music, if you can call it that, physically assaults anyone dumb enough to listen 🎧

  • Christophe Warum kommt man nicht darum herum? Also was ist der konkrete Einsatz von globalem Zustand? Denn in aller Regel kommt man da sehr wohl darum herum.

    Um global kommt man ziemlich sicher herum. Das macht auch nichts im ganzen Programm bekannt, sondern sagt erst einmal nur, dass die Funktion oder Methode in der man einen Namen so deklariert, diesen Namen auf Modulebene neu binden kann. Was ziemlich undurchsichtig ist, das Funktionen/Methoden auf magische Weise etwas verändern. Weshalb man das so nicht machen sollte.

    Objekte sind auch nicht automatisch globaler Zustand. Auf die Attribute kann man nur zugreifen wenn man Zugriff auf das Objekt hat. Also eigentlich nur wenn es der Funktion oder Methode als Argument übergeben wurde. Denn sonst wäre das ja globaler Zustand, den man vermeiden sollte.

    Dennis89 In dem Szenario lohnt es sich auch erst einmal zu schauen ob das sinnvoll ist 11 Funktionen tiefe Aufrufhierarchien zu haben. Da besteht die Chance das zumindest einige davon falsch strukturiert sind und statt ein Ergebnis zurück zu geben, den nächsten Schritt in der Verarbeitung selbst anstossen.

    Und es ist auch nicht die Anzahl der Übergaben, denn wenn das einen Wert betrifft, macht eine Klasse keinen Sinn. Das Kriterium ist hier eher die Anzahl der Argumente und ob es Sinn macht zusammengörende Werte zu einem Objekt zusammen zufassen. Und das kann auch schon bei einem einzigen Aufruf Sinn machen.

    "He who laughs last thinks slowest"

  • Ich finde dies Thema interessant.. __blackjack__ Danke für Deine Erklärungen, um es besser zu verstehen habe ich nach einem

    Beispiel gesucht (den ich bisher übersichtlich fand :|:( siehe code unten... (einen Teil habe ich gelöscht).

    Wie würdest Du dies schreiben?


  • devil P Ich würde das mit aufgeräumteren Importen schreiben. Da bleiben nur vier Zeilen übrig. Da Code entfernt wurde kann man natürlich nicht sagen was vielleicht doch gebraucht wird. Aber wenn Thread beispielsweise tatsächlich benutzt wird, dann ist globaler Zustand ja noch schlimmer, weil noch unübersichtlicher und noch fehleranfälliger.

    Dann wie schon gesagt ohne globalen Zustand. Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die main() heisst. Und global hat in einem sauberen Programm nichts zu suchen. Alles was Funktionen und Methoden ausser Konstanten benötigen wird als Argument(e) übergeben. Ergebnisse werden an den Aufrufer zurückgegegeben statt die magisch globalen Namen zuzuweisen.

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

    Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen. pw sollte wohl beispielsweise power heissen. Wobei ich es mir hier mit dieser Variable mal einfach mache: die sollte nicht global sein. Da sie aber nirgends im gezeigten Quelltext verwendet wird, schmeisse ich sie einfach raus, und den Code der die beeinflusst auch. Das müsste man objektorientiert lösen. Wie das konkret aussehen könnte kommt auf den Kontext an. Da ich den nicht kenne: weg damit. 😜

    Ähnliches Problem mit der buttons()-Funktion. Eine ”Funktion“ die weder Argumente bekommt, noch ein Ergebnis liefert, riecht komisch. Da ist ziemlich sicher was faul, im Sinne von: da wird wahrscheinlich irgend ein globaler Zustand geändert.

    „Setup“ ist im englischen ein Wort, also setup_thunder_borg().

    sys.exit() sollte nicht missbraucht werden um sich einen ”Notausgang” in irgendwelchen Funktionen zu schaffen weil man sich eine saubere Fehlerbehandlung sparen möchte. Die Setup-Funktion sollte an der Stelle am besten eine Ausnahme auslösen, damit der Aufrufer entscheiden kann was passieren soll. Das kann dann in der Hauptfunktion auch ein sys.exit() sein, dann aber mit einem Rückgabecode ≠0, denn dafür ist diese Funktion gedacht: dem aufrufenden Prozess einen Rückgabecode zu übermitteln der zumindest potentiell ≠0 ist.

    else: pass ist unnötiger Code.

    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.

    Falsche Kommentare sind schlimmer als keine Kommentare. Ein Kommentar soll eventuelle Fragen mit dem Code klären, aber wenn dort falsche Informationen drin stehen, oder gar welche die dem Code direkt widersprechen, erreicht man genau das Gegenteil. Der Leser weiss dann nicht was falsch ist, der Code oder der Kommentar. Beispiel aus dem Quelltext: voltageOut= 12.0 * 0.60 kommentiert mit „we limit it to 95%“. Was ist jetzt richtig? Die 0,6 im Code oder die 95% im Kommentar?

    Wobei das alles gar nicht verwendet wird und seq eine Konstante auf Modulebene sein kann. Aber egal wo — mit einem besseren Namen als seq. Wenn man mit einem Kommentar erklären muss was der Wert bedeutet, sollte man immer noch mal schauen ob man den Namen nicht besser wählen kann und sich so den Kommentar sparen kann. Da wird kommentiert, das es sich um Paare aus „drive levels“ handelt. Also besser drive_level_pairs, statt seq.

    Da ist ein Haufen Funktionen die alle das gleiche machen, nur mit einem unterschiedlichen magischen Index. Erstens sollte es diesen magischen Index nicht geben, denn das ist undurchsichtig. Und es sollte eine Funktion geben, der man die Aktion übergibt.

    In forward() ist wohl auch ein Fehler weil dort beide male der zweite Index den Wert 0 hat. Das funktioniert natürlich weil das Paar zweimal den gleichen Wert enthält, aber es ist halt doch ein bisschen verwirrend ob das nun so sein soll oder nicht.

    Fensternamen sollten nicht mehrfach als Zeichenketten im Programm stehen. Dafür würde man besser eine Konstante definieren. Genau so wie magische Werte wie 27 + Kommentar, dass das für die Escape-Taste steht. Eine entsprechend benannte Konstante erübrigt den Kommentar.

    Zwischenstand (ungetestet):

    Ich konnte mich hier noch um eine Klasse drücken, weil pw & Co rausgeflogen sind. Damit käme man nicht mehr um eine Klasse herum. Wie die heisst, und was da rein gehört, kommt aber auf Kontext an, den nur Du kennst.

    Ist das ständige Aufrufen von SetCommsFailsafe() nötig/sinnvoll? Reicht es nicht das einmal bei der Initialisierung aufzurufen? Und was ist der Default-Wert? Ich habe eine soweit ich das sehen kann aktuellere API im Netz gefunden wo steht, dass False die Voreinstellung ist. Die API verwendet zudem Namen die den Namenskonventionen entsprechen, also set_comms_failsafe().

    Das mit TEST als Bedingung für print()-Ausgaben sieht nach einem Fall für logging aus. Beispielsweise mit dem logging-Modul aus der Standardbibliothek oder etwas externen wie loguru.

    "He who laughs last thinks slowest"

  • __blackjack__ : Python Version: 3.7.3 ersetzen des None durch object() keien Änderung

    Möglicherweise liegt das Problem aber woanders im Code ?? daher poste ich jetzt doch mal

    den kompletten Code, Ziel ist auf einem Display mit mouseclick Event den thunder_borg zu steuern.

    Wenn ich den Beitrag "Werte des Shelly ..." richtig verstanden habe, wirst Du den Code nicht mögen :)

    Der wesentliche Punkt ist aber , den "thunder_borg" in mouseclick zu "übertragen" ich hatte damals in

    meiner Verzweifelung daher TB/ thunder_borg als global definiert, was funktionierte, aber ich verstehe

    jetzt nicht optimal ist.

  • devil P Meh, da ist das komische Verhalten von Numpy-Arrays Schuld die keinen einzelnen Wahrheitswert liefern wenn man das Array mit einem Wert vergleicht, sondern wieder ein Array mit Wahrheitswerten. Damit funktioniert dann die Variante von iter() mit zwei Argumenten nicht. ☹️

    Es sind unbenutzte Importe im Quelltext. Lock, Thread, time, sleep, und plt1. Letzteres sollte auch wenn es verwendet würde, nicht so heissen. Und time.time() wird in auskommentiertem Code benutzt um eine Zeitspanne zu ermitteln: dafür benutzt man time.monotonic().

    Die __init__()-Methode zu Move ist falsch eingerückt.

    buttons und pushbutton wären gute Namen für Buttons und einen Pushbutton, aber nicht für Funktionen. Funktionen (und Methoden) werden üblicherweise nach der Tätigkeit benannt die sie durchführen, damit man weiss was sie tun und um sie leichter von eher passiven Werten unterscheiden zu können.

    Man nummeriert keine Namen. Entweder will man sich da bessere Namen überlegen oder gar keine Einzelnamen und -werte, sondern eine Datenstruktur. Oft eine Liste. So auch bei img1 bis img6.

    Das ist ein bisschen komisch das die x-Werte in mouseclick() anscheinend unterschiedliche ”längen” beschreiben, wo doch das Bild für alle Buttons das gleiche ist‽ Diese Werte sollte man auch nicht von Hand hart in den Quelltext schreiben, sondern die sollten sich an den Buttongrössen zur Laufzeit orientieren.

    Warum werden in mouseclick() die Argumente x und y noch mal in mouseX und mouseY umbenannt?

    Das event dort kann nur einen Wert haben, also elif beim zweiten if.

    pw habe ich wieder rausgeworfen, weil das nirgends verwendet wird. 😈

    Ungetestet:

    "He who laughs last thinks slowest"

  • Meine Frage wurde eigentlich beantwortet. Ich habe einen Beispiel code erstellt:

  • Christophe Neee, das ist nicht gut. self ist der Name des ersten Arguments von Methoden, nicht von Funktionen. Und man sollte auch nicht einfach so an irgendwelche ”fremden” Objekte neue Attribute binden, die die eigentlich gar nicht haben. Und was wirklich richtig falsch ist, ist das ändern der GUI von einem Thread aus. GUIs sind in der Regel alle nicht threadsicher und man darf die nur von dem Thread aus verändern, in dem die GUI-Hauptschleife läuft.

    Der Thread macht aber auch überhaupt gar keinen Sinn. Die while-Schleife wartet da ja nur darauf das sich der Wert des Textes auf dem Button ändert, was passiert wenn man den drückt — da hat man dann aber schon das Ereignis auf das man reagieren kann, da muss man nicht die CPU mit warten beschäftigen.

    system_variables() ist auch kein Funktionsname. Funktionen und Methoden werden in der Regel nach der Tätigkeit benannt die sie durchführen, damit der Leser a) weiss was die machen und sie b) leicht von eher passiven Werten unterscheidbar sind.

    Man sollte Namen nicht kryptisch abkürzen und auch sinnvoll benennen. btn_val wäre auch als button_value nicht wirklich sinnvoll benannt. Denn das ist ja gar kein Wert für/von dem Button, sondern die Bedeutung dieses Wertes ist der Index in die Farben, also eher so etwas wie color_index. Und sowohl der Index als auch die Farben beziehen sich eher auf den Hintergrund als auf den Button.

    Der Index in die Farben wird auch an zwei Stellen im Code verwendet an denen die Länge der Farbsequenz hart kodiert eine Rolle spielt. Wenn man da mal eine Farbe hinzufügt oder weg nimmt, muss man aufpassen das man diese Berechnungen auch überall anpasst. Das sollte nicht sein. Es sollte reichen einfach nur die Liste mit den Farben zu ändern. Der restliche Code sollte damit ohne weitere Änderungen klar kommen können.

    Auch das erstellen des Textes für den Button ist zweimal im Code vorhanden. Da muss man beim schreiben und beim verändern immer darauf achten das an beiden Stellen gleich zu machen. Das ist fehleranfällig und macht unnötig Arbeit. Solche Redundanzen vermeidet man beim Programmieren deshalb.

    Sooo, und jetzt bitte nicht einfach alles in einer Gott-Klasse an self binden, denn das wäre letztlich die Probleme mit globalen Variablen einfach nur eine Ebene weiter in eine Klasse verschoben.

    "He who laughs last thinks slowest"

  • Alternativ könnte man auch die beiden Werte für das Durchschalten der Farben in einem allgemeineren Datentyp kapseln und käme dann bei der GUI nur mit „Closures“ aus:

    Etwas ähnliches wie Cycler gibt es in der Standardbibliothek mit itertools.cycle() und noch näher dran kommt eine collections.deque.

    "He who laughs last thinks slowest"

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!