Was ist eine globale Variable und warum soll man sie nicht benützen?

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.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ja. Wo immer das geht. Es ist oft ein Zeichen vermurksten Designs wenn alle alles kennen müssen. Dieses durchreichen stoppt also recht schnell, wenn man vernünftig arbeitet.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__deets__ hat geschrieben:Ja. Wo immer das geht. Es ist oft ein Zeichen vermurksten Designs wenn alle alles kennen müssen. Dieses durchreichen stoppt also recht schnell, wenn man vernünftig arbeitet.
Also für eine tkinter Gui habe ich jetzt die Lösung gefunden. Es reicht, wenn man so ein Signalsystem in der obersten Ebene gekapselt hat statt es global zu haben oder es überall bei der Instanzierung von Objekten als Parameter mit weiterzureichen. Mit der folgenden Funktion - das ist jetzt keine Variable - kann man dann auf das Root Objekt zugreifen:

Code: Alles auswählen

def root(gui_element):
    root = gui_element   
    while root.master:
        root = root.master
    return root
Wenn man lang genug sucht, findet man doch eine Lösung, die nicht global ist.

PS: alle sollen nicht alles kennen, aber das zentrale Kommunikationssystem sollen alle kennen. Die Telefonnummern von jedem sollen nicht alle kennen, aber das Telefon soll man schon kennen und die Telefonnummern von seinen Freunden und Bekannten.
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Alfons Mittelmeyer hat geschrieben: [...] ob diese zentralen Signalsysteme global zur Verfügung stehen dürfen oder ob man sie überall als Parameter mit übergeben soll?
Das kommt darauf an, welche Architektur so ein Programm hat. Man könnte sowas mittels Dependency Injection lösen, also mittels Parameter, die man dann überall mitschleift. Man könnte daraus ein Parameter-Objekt machen, das diese ganzen Sub-Systeme zu einer einzigen Struktur zusammen fasst. Damit würde man aber den Grad der Isolation abschwächen, da dann jede Funktion, die dieses Objekt übergeben bekommt, auf jedes beliebige Sub-System zugreifen könnte. Wenn man genügend Disziplin besitzt, muss das aber kein Problem sein.

Letzteres hatte ich mal testweise in Clojure umgesetzt. Dabei repräsentierten Components (Services) die Aussenwelt, also zB. Datenbanken, und aller veränderbare Zustand war ausschließlich dort zuhause. Die Geschäftslogik war rein funktional und eventbasiert. Das muss man sich so vorstellen: ein Command (ein dummes Dictionary, das ein Type-Tag und alle relevanten Daten enthält) wird empfangen, dazu wird eine passende Handler-Funktion gesucht und aufgerufen. Diese Funktion erzeugt dann aus den Daten im Command und dem aktuellen Zustand der Welt eine Liste von Events, die sie als Ergebnis zurückgibt. Sonst macht diese Handler-Funktion nichts, dh., sie ändert nicht den Zustand der Welt. Die Ergebnis-Events werden dann über einen Eventbus verschickt. Event-Handler können Events vom Bus abonnieren und werden aufgerufen, sobald ein entsprechender Event eintritt. Diese Handler erst dürfen den Zustand der Welt verändern, indem sie zB. Daten in eine Datenbank schreiben.

Zusätzlich habe ich Dependency Injection in den Command-Handlern mittels der Reader-Monade realisiert und diese zur Fehlerbehandlung in einen Error-Monad-Transformer gewickelt. Das sieht dann etwa so aus:[codebox=clojure file=Unbenannt.txt](def-command ::customer/place-order
:req [::customer/id
::order/id])

(def-failure ::customer/has-given-no-address
:req [::customer/id])

(def-failure ::customer/cart-is-empty
:req [::customer/id])

(def-failure ::customer/has-selected-no-payment-method
:req [::customer/id])


(defn subscriptions
[component]

{::order/placed
(store-in (:repository component) ::customer/id)

::customer/place-order
(fn [{customer-id ::customer/id
order-id ::order/id}]
(within (boundary component ::customer/shop)
(fail-if-exists ::order/id order-id)
customer <- (get-entity ::customer/id customer-id)
(mdo-await*
(mwhen (-> @customer ::customer/cart empty?)
(fail-with ::customer/cart-is-empty
::customer/id customer-id))
(mwhen (-> @customer ::customer/address nil?)
(fail-with ::customer/has-given-no-address
::customer/id customer-id))
(mwhen (-> @customer ::customer/payment-method nil?)
(fail-with ::customer/has-selected-no-payment-method
::customer/id customer-id)))
(return [(event ::order/placed
::customer/id customer-id
::order/id order-id
::order/items (@customer ::customer/cart)
::order/address (@customer ::customer/address)
::order/payment-method (@customer ::customer/payment-method))
(event ::customer/cart-cleared
::customer/id customer-id)])))})[/code]
In der Handler-Funktion für ::command/place-order werden nirgends explizit Repositories, Datenbanken, etc. erwähnt, obwohl deren Informationen dort verwendet werden. Wie monadische Funktionen funktionieren kann ich allerdings hier nicht erklären. Monaden kann man IMO überhaupt nicht erklären. Man kann sie auch nicht verstehen. Man kann sich nur an sie gewöhnen.

Bild
In specifications, Murphy's Law supersedes Ohm's.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

pillmuncher hat geschrieben:
Alfons Mittelmeyer hat geschrieben: [...] ob diese zentralen Signalsysteme global zur Verfügung stehen dürfen oder ob man sie überall als Parameter mit übergeben soll?
Das kommt darauf an, welche Architektur so ein Programm hat. Man könnte sowas mittels Dependency Injection lösen, also mittels Parameter, die man dann überall mitschleift. Man könnte daraus ein Parameter-Objekt machen, das diese ganzen Sub-Systeme zu einer einzigen Struktur zusammen fasst. Damit würde man aber den Grad der Isolation abschwächen, da dann jede Funktion, die dieses Objekt übergeben bekommt, auf jedes beliebige Sub-System zugreifen könnte. Wenn man genügend Disziplin besitzt, muss das aber kein Problem sein.
Dass jeder auf alles zugreifen kann, wird bei komplexen Systemen unterbunden. Da wird das Bussystem von der Admistration so konfiguriert, dass bestimmte Hardware- oder Softwarekomponenten nur solche Messages mit den Funktionsblöcken bekommen, für die sie zuständig sind. Teilkomponenten davon sollen dann wiederum nur ein Subset bearbeiten und initialisieren dann nur Zugriffe für ihre Funktionsblöcke - das ist so etwas, wie die Vorwahlen für die Signale. Dass man auch andere Funktionsblöcke oder andere Messages verwendet, die nichts mit den Aufgaben der Teilkomponente zu tun haben, wird durch Codereview unterbunden. Außerdem würde man das an Trace Messages sehen, welche den Messageaustausch und die Registrierung für Funktionsblöcke mitprotokollieren. Man kann also nicht zugreifen, worauf man will, denn es ist genau spezifiziert, was ein Softwaremodul zu tun hat.

PS: für eine kleinere Anwendung ist wohl der Aufwand für eine Unterteilung in Vorwahlen und Messagenummer im Vorwahlbereich und Filterung nach erlaubten Vorwahlen unnötig.
BlackJack

@Alfons Mittelmeyer: Man muss dieses Rad nicht neu erfinden, dafür haben Widgets bereits eine Methode. Wobei man die selten bis gar nicht brauchen wird oder sollte. Beliebige Widgets sollten nicht am Toplevelfenster manipulieren wollen. Wenn das jeder machen würde kämen die sich sicher schnell in die Quere.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Man muss dieses Rad nicht neu erfinden, dafür haben Widgets bereits eine Methode. Wobei man die selten bis gar nicht brauchen wird oder sollte. Beliebige Widgets sollten nicht am Toplevelfenster manipulieren wollen. Wenn das jeder machen würde kämen die sich sicher schnell in die Quere.
Ach so, diese Methode winfo_toplevel funktioniert nicht. Für ein Menü ist, kommt dabei das Menü heraus und nicht das Anwendungsfenster.

Es geht nicht um Widgets es geht um Interkommunikation. Eine Software könnte in verschiedene Teilapplikationen unterteilt sein - das nenne ich einmal Applications. Da könnte es eine Kommunikation innerhalb einer Applikation und auch zwischen den Applikationen geben. Und beliebige Widgets sollten natürlich nicht am Toplevelfenster manipulieren. Es kann natürlich sein, und wird auch meist so sein, dass ein Druck auf einen Menübutton Auswirkungen auf Teile der Gui hat. Wo diese Gui Teile sind, geht dem Menübutton meiner Meinung nach gar nichts an, der sollte über den inneren Aufbau der Gui gar nichts wissen.

Es geht darum, dass der Druck auf den Menübutton (command) das veranlaßt, was er soll, ohne dass die Callbackroutine für den Menübutton kreuz und quer in der Gui umeinandfummelt. Wie gesagt, der Gui Aufbau soll für das Menü verborgen bleiben!!!

Und das geht jetzt auch ohne globale Variablen. Man definiert einfach, dass eine Applikation bestimmte Attribute und Methoden hat und dass Bestandteile der Applikation darauf zugreifen können sollen, sofern das benötigt wird. Das sind dann keine globalen Variablen, sondern applikationsweit existierende Objekte. Die Referenz auf die Applikationsebene, das kann die Ebene des Anwendungsfensters sein, oder auch darüber außerhalb der Gui oder auch darunter für einen Teil der Gui, wird dann jeweils bei der Instanzierung von Objekten übergeben.

Und nicht jedes Objekt kann auf alles zugreifen, sondern nur die Objekte, denen man die Referenz für das Signalsystem übergeben hat. Das Signalsystem behandelt auch nur gewisse Meldungen, von einem Zugriff auf Alles kann da keine Rede sein.

OK, also am Toplevelfenster haben die widgets nichts zu machen, dann ist das so:

Code: Alles auswählen

import tkinter as tk

class Application():
    def __init__(self):
        self.callbacks = {}

class Gui(tk.Tk):
    def __init__(self,application):

        # Application
        self.application = application

        tk.Tk.__init__(self)
        self.attributes('-zoomed', True)
        self.grid_columnconfigure(0, weight=1)

        # Frames
        self.one = ExampleClassOne(self)
        self.two = ExampleClassTwo(self)
 
        # menubar
        self['menu'] = MenuBar(self,self.application)
 
        # register callbacks
        self.application.callbacks['example_method_one'] = self.example_method_one
        self.application.callbacks['example_method_two'] = self.example_method_two

    def example_method_one(self):
        self.two.grid_forget()
        self.one.grid()
 
    def example_method_two(self):
        self.one.grid_forget()
        self.two.grid()
 
 
class ExampleClassOne(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        one = tk.Label(master=self,
                       text='Test 1',
                       font=('Monospace', 20))
        one.grid(row=0)
 
 
class ExampleClassTwo(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        two = tk.Label(master=self,
                       text='Test 2\nTest 2\nTest 2')
 
        two.grid(row=0)
 
 
class MenuBar(tk.Menu):
    def __init__(self, master=None, application=None):
        self.application=application
        tk.Menu.__init__(self, master)
 
        # submenu
        self.file = tk.Menu(master)
        self.example = tk.Menu(master)
 
        # submenu cascades
        self.add_cascade(label='Datei', menu=self.file)
        self.add_cascade(label='Beispiel', menu=self.example)
 
        # submenu file
        self.file.add_command(label='Beenden', command=self.quit)
 
        # submenu example
        self.example.add_command(label='Beispiel 1', command=self.example_method_one)
        self.example.add_command(label='Beispiel 2', command=self.example_method_two)

    def example_method_one(self):
        self.application.callbacks['example_method_one']()
 
    def example_method_two(self):
        self.application.callbacks['example_method_two']()
 
 
def main():
    Gui(Application()).mainloop()
 
 
if __name__ == '__main__':
    main()
Abgewandeltes Beispiel von Schwarzer Wolf aus dem tkinter Forum: viewtopic.php?f=18&t=40332
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Alfons Mittelmeyer:
Globaler Zustand meint eigentlich das Einbringen eines äusseren Zustandes in etwas Inneres. Das Innere kann in Python der Kontext eines Modul, einer Klasse oder einer Funktion/Methode sein. Dem begegnest Du allerorten - Modulimporte, Funktion- oder Klassendefinitionen etc. Diese sind in der Regel konstant, d.h. ein `from a import A` gibt Dir immer das gleiche unveränderte Objekt hinter `A`, ein `do_something(); do_something(); do_something();` sollte 3x auf das gleiche unveränderte Funktionsobjekt zeigen.

Probleme bekommst Du, wenn der äussere Zustand veränderlich ist bzw. prinzipiell einen undefinierten Zustand haben kann - dann sind alle weiteren Aktionen prinzipiell auch undefiniert. Als überspitztes Bsp - was würdest Du sagen, wenn `print "Hello World"` plötzlich Deine Festplatte formatiert? Daher gilt grundsätzlich, dass der äussere Zustand so gut es eben geht wohl definiert sein soll - das geht verlässlich nur mit Konstanten. Weil nur konstanter äusserer Zustand nicht zielführend ist, hat man sich Interfaces überlegt, welche bessere Kapselung schaffen - z.B. Funktionen mit Parametern und Rückgabewerten. Bedenke, dass jedwedes veränderliche (mutuable) Objekt als Parameter einer Funktion immer noch ein globaler Zustand ist, und alle Probleme erbt, wenn das Objekt in der Funktion modifiziert wird (nicht reentrant). Diese "Verletzung" wird allerdings hingenommen, da ein Objekt für sich eine Kapselung von Code und Daten (Zustand) darstellt und ein solches Vorgehen den Kontrollfluß nachvollziehbar hält - das nennt man dann OOP.

In low level Programmierung funktioniert das nicht mehr selbstverständlich, da Interrupthandler auch mal den `this`-Pointer in C++ verbiegen können, häufigste Folge dürfte ein segfault sein, ggf. auch ein Angriff auf das System. Damit wird offensichtlich, dass auch objektweite Sicht- und Modifizierbarkeit schon ein Problem darstellen kann. C hat mit lokalen `static` Variablen noch eine Besonderheit, die es zu vermeiden gilt - der Wert ist ist zwar nur lokal sicht- und veränderbar, "klebt" aber im Speicher und steht bei der nächsten Ausführung zur Verfügung. Daher ist es ein versteckter globaler Zustand.

Zum Glück werden diese Probleme von der libc und Python vor uns versteckt - übrig bleiben die klassischen veränderlichen globals in Python. Und da geht es am ehsten um Nachvollziehbarkeit des Kontrollflusses.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@jerch: danke für Deine Auskünfte. Es geht als nicht darum, ob etwas modul global ist oder zwar gekapselt ist, aber dennoch applikationsweit verfügbar ist. Das kommt eigentlich fast auf dasselbe hinaus. Es kommt darauf an, dass sich alles spezifizert verhält. Und Globales möglichst konstant ist.

Wenn man einer Variablen nur einmal einen Wert zuweist und diesen nicht verändert, dann ist das eine Konstante.
Wenn man aber einem dictionary einen Wert zuweist, etwa english['hallo'] = 'hello', und diesen nicht verändert, kann man auch von einer Konstante sprechen.

Wenn man später das dictionary erwitert, etwa mit english['Katze'] = 'cat', dann hat man den Wert für 'hallo' nicht verändert. Man hat nur seine Konstantendefinitionen erweitert.

Es ist allso völlig fehl am Platze, wenn dann von einigen das Geschrei losgeht, dass wenn man das Dictionary erweitert, es zu einer Variablen wird, und das soll man nicht tun.

Ich hoffe, dass man in der Lage ist, zu erkennen, dass die Erweiterung von Konstantendefinitionen, nicht daraus Variablen macht.
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: ich weiß, auf welche Spizfindigkeiten Du hier anspielst, aber wenn in einer Funktion ein globales Wörterbuch verändert wird, ist eben nicht sichergestellt, dass das danach Konstant bleibt. Bei Deinem Beispiel ist sowohl Katze als auch cat eine Konstante und daher auch die Zuweisung eine Konstante. Nimmt man aber statt "cat" self.example_method_one ist das keine Konstante mehr, damit kann man auch nicht behaupten, das Wörterbuch wäre konstant. Es gibt Fälle, wo man dynamisch Konstanten erzeugen könnte, aber dann sieht der Code dazu so aus:

Code: Alles auswählen

ENGLISH = read_dictionary("english.txt")
Das heißt, die Konstante ENGLISH bekommt *einmal* einen Wert, der sich aber nach der Definition nie wieder ändert.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Alfons Mittelmeyer hat geschrieben:Wenn man aber einem dictionary einen Wert zuweist, etwa english['hallo'] = 'hello', und diesen nicht verändert, kann man auch von einer Konstante sprechen.
Ja, das Item ['hallo'] ist dann konstant. Da es in Python keine echten Konstanten gibt, geht es nur per Konvention, den Wert nicht mehr zu ändern.
Alfons Mittelmeyer hat geschrieben:Wenn man später das dictionary erwitert, etwa mit english['Katze'] = 'cat', dann hat man den Wert für 'hallo' nicht verändert. Man hat nur seine Konstantendefinitionen erweitert.
Da wird es tricky, ['hallo'] ist konstant, der Container `english` aber nicht mehr. Das kann dann zu Problemen führen, wenn sich nachfolgender Code auf das (Nicht-)Vorhandensein von Items verlässt, ohne es erneut zu prüfen. Das ist ein typisches Problem, was man sich mit einem "world-object" einhandelt.
Ürbigens ist es völlig legitim, Konstanten schrittweise zu initialisieren, wenn es sich anbietet. Das sollte aber vor der ersten Benutzung im Hauptkontrollfluß fertig sein, also nix nachträglich ranfummeln:

Code: Alles auswählen

# init - Konstanten
USERS = ['A', 'B']
if xy:
    USERS.append('C')  # hier ok

# Hauptkontrollfluß
def main():
    ....# macht was mit USER
    USER.append(D)  # hier nicht ok
    ....# macht was mit USER - Achtung USER != USER
Falls es unvermeidlich ist (meist wegen falschem Design), dann muss sowas unbedingt dokumentiert werden, sodass alle (auch man selbst) drüber stolpert. Für Bibliothekscode zur Benutzung durch Andere ist sowas generell "verboten".
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jerch hat geschrieben:Übrigens ist es völlig legitim, Konstanten schrittweise zu initialisieren, wenn es sich anbietet. Das sollte aber vor der ersten Benutzung im Hauptkontrollfluß fertig sein, also nix nachträglich ranfummeln
Was ist der Hauptkontrollfluß?

Ist dies das, was bei tkinter in der mainloop geschieht? Vorher, beim Aufbau der Gui - vor mainloop - sollen Verweise auf bestimmte Methoden bestimmter Objekte in das Dictionary eingetragen werden, unmittelbar nach der Erzeugung der Objekte.
BlackJack

Damit ist der Code gemeint der auf Modulebene ausgeführt wird wenn das Modul importiert wird und der nur Konstanten, Funktionen, und Klassen definiert.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Alfons Mittelmeyer hat geschrieben:Ist dies das, was bei tkinter in der mainloop geschieht? Vorher, beim Aufbau der Gui - vor mainloop - sollen Verweise auf bestimmte Methoden bestimmter Objekte in das Dictionary eingetragen werden, unmittelbar nach der Erzeugung der Objekte.
Mit tkinter kenne ich mich zu wenig aus, daher kann ich dazu nicht viel sagen.

Eine andere populäre GUI-Bibliothek ist Qt/PyQt. Diesem merkt man die Herkunft von C++ teilweise noch an, da es eine ziemlich strikte Trennung von Definitionen und Ausführungslogik möchte:

Code: Alles auswählen

# Definitionsteil
import ...

SOME_CONST = ...

class XY(QObject):
    ...

def functionXY():
    ...

# Einsprung für Ausführung (als Hauptskript)
# ab hier keine neuen Klassen, Konstanten oder Funktionen mehr,
# sondern nur noch Benutzung des Definierten (gilt auch für modulweite Importe)
if __name__ == '__main__':
    app = QApplication()
    xy = XY()
    ...
Was man hin und wieder sieht ist ähnlich wie (bitte nicht nachmachen):

Code: Alles auswählen

import ...

app = QApplication()

class XY(QObject):
    ....
    def do_something_with_SOME_GLOBAL(self):
        global SOME_GLOBAL
        ...

xy = XY()
SOME_GLOBAL = ...
import z
xy.do_something_with_SOME_GLOBAL(z.foo)
Zweiteres verursacht unbestimmtes Verhalten, da Qt es nicht mag, wenn Klassen nach `QApplication` erstellt werden. Python an sich verbietet das nicht. Hintergrund bei Qt sind übrigens globale Zustände, bei denen die Reihenfolge der Abarbeitung plötzlich wichtig wird ;). (In C++ selbst ist dies kein Problem, da zur Laufzeit alle Typen/Klassen bekannt sind.)
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jerch hat geschrieben:[Ürbigens ist es völlig legitim, Konstanten schrittweise zu initialisieren, wenn es sich anbietet. Das sollte aber vor der ersten Benutzung im Hauptkontrollfluß fertig sein, also nix nachträglich ranfummeln
Es geht nicht um ranfummeln. Fummeln wäre, wenn die Schnittstellen nicht für nachträgliche Registrierungen ausgelegt wären. Wenn die Schnittstellen aber dafür ausgelegt sind, dann ist es ein spezifiziertes Verhalten.

Ein Temperatursensor darf ruhig die Außentemperatur senden, auch wenn noch keine Anzeige dafür aktiv ist. Er darf aber natürlich keine noch undefinierte Callbackfunktion aufrufen.

Durch entsprechende Interfaces kann man dafür sorgen, dass es keine undefinierten Zustände gibt

Ein einfaches dictionary würde für einfache überschaubare Applikationen genügen.
Jedoch für größere Applikationen wäre dieses ungeeignet

Code: Alles auswählen

# Für eine kleine Anwendungen mit genau einer Anzeige durchaus ausreichend und sinnvoll
vehicle.statusbus['aussentemperatur'](23)
# Falsch für komplexe Anwendungden, denn dieses würde crashen, wenn noch keine Callbackfunktion registriert ist
# ausserdem funktioniert das auch nur bei genau einer Anzeige

# Auch für komplexe Anwendungen geeignet
vehicle.statusbus.broadcast("aussentemperatur",23)
# Richtig, alle dafür registrierte Anzeigen erhalten die Nachricht
# Ist noch keine Anzeige registriert, dann wird auch kein Callback aufgerufen
# Wer alles diese Information erhält, geht dem Temperatursensor nichts an
# Ausserdem ist in einem solchen Interface auch implementiert, dass bei Entfernung von Anzeigen
# auch die Registrierungen für deren Empfangsnachrichten wieder gelöscht werden
# und somit keine nicht mehr vorhandenen Callbackfunktionen aufgerufen werden.
# Das wäre etwa der Fall, wenn der User ein Toplevel Window schliesst
# und dadurch zerstört. Die verwendeten abgeleiteten Klassen für Gui Container
# nehmen in ihrer destroy Methode auch die Deregistrierung der für sie registrierten
# Empfangsnachrichten vor. Gleiche Empfangsnachrichten für andere Empfänger bleiben dabei unberührt
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: schön, Dein Message-Broker, wobei Zeile 2 und 7 nur verschiedene Varianten sind, wie man einen Aufruf schreiben könnte. Es fehlt noch vehicle.statusbus.aussentemperatur(23). Aber was hat das jetzt mit dem Thema des Threads „globale Variablen“ zu tun?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: schön, Dein Message-Broker, wobei Zeile 2 und 7 nur verschiedene Varianten sind, wie man einen Aufruf schreiben könnte. Aber was hat das jetzt mit dem Thema des Threads „globale Variablen“ zu tun?
Ja das möchte ich gerne von Dir wissen, denn Du bist es ja, der schrieb:
Sirius3 hat geschrieben:Nimmt man aber statt "cat" self.example_method_one ist das keine Konstante mehr, damit kann man auch nicht behaupten, das Wörterbuch wäre konstant.
Hier ist ja der Fall, dass später noch Appplikationen mit Anzeigen dafür hochfahren und entweder gleich eine noch nicht eingetragene Message oder nur noch sich als zusätzlichen Empfänger für bereits eingetragene Messages (vom Interface her kein Unterschied) zusammen einer eigenen Callback Funktion eintragen.

Und da wurde global variabel soll man nicht machen und nachträglich ranfummeln soll man auch nichts, weil es dann eine Variable wäre, mehrfach geäußert.
Zuletzt geändert von Alfons Mittelmeyer am Mittwoch 26. April 2017, 19:52, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

Sirius3 hat geschrieben:Und da wurde global soll man nicht machen und nachträglich ranfummeln soll man auch nichts, mehrfach geäußert.
Und was folgerst Du daraus? vehicle und vehicle.statusbus sind keine Konstanten.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:
Sirius3 hat geschrieben:Und da wurde global soll man nicht machen und nachträglich ranfummeln soll man auch nichts, mehrfach geäußert.
Und was folgerst Du daraus? vehicle und vehicle.statusbus sind keine Konstanten.
Genau, das habt ihr gesagt und weiter, dass man deshalb nichts mehr anfügen soll.

Wenn ich eine Texteditor habe und Text schreibe, dann ist der Text auch keine Konstante, soll man den gleich lassen?
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: niemand hat geschrieben, dass man keine Variablen benutzen darf, nur global sollen sie nicht sein.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Sirius3: Vieleicht komme ich jetzt langsam dahinter. Wenn ich einen Texteditor schreiben würde, der nur ein einziges Fester zum Bearbeiten genau eines Textes hätte, dann könnte man den Inhalt dieses Fensters als global ansehen. Wenn man ihn allerdings so schreibt, dass man auch mehrere Fenster haben könnte, dann wären die Zustände fensterspezifisch, also interne Zustände.

Weiterhin könnte man aber den Texteditor für nur ein Fenster so implementieren, dass man auch weitere Fenster instanzieren könnte, wenn man wollte. Und dann wären das wohl auch interne Zustände.

Es geht also darum, dass man auch dann objektorientiert programmieren soll, also mit möglicher Instanzierung, auch wenn das im betreffenden Anwendungsfall unnötig wäre oder auch gar nicht ginge?

So kann man etwa mit tkinter meist nicht mehrere GUIs also mehrere Anwendungsfenster haben, aber man sollte wohl so implementieren, als ob man das könnte?
Antworten