Was ist eine globale Variable und warum soll man sie nicht benützen?
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
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:__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.
Code: Alles auswählen
def root(gui_element):
root = gui_element
while root.master:
root = root.master
return root
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.
- pillmuncher
- User
- Beiträge: 1530
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
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.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?
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.

In specifications, Murphy's Law supersedes Ohm's.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
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.pillmuncher hat geschrieben: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.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?
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.
@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.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Ach so, diese Methode winfo_toplevel funktioniert nicht. Für ein Menü ist, kommt dabei das Menü heraus und nicht das Anwendungsfenster.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.
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()
@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.
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.
-
- 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.
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.
@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:
Das heißt, die Konstante ENGLISH bekommt *einmal* einen Wert, der sich aber nach der Definition nie wieder ändert.
Code: Alles auswählen
ENGLISH = read_dictionary("english.txt")
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 aber einem dictionary einen Wert zuweist, etwa english['hallo'] = 'hello', und diesen nicht verändert, kann man auch von einer Konstante sprechen.
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.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.
Ü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
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Was ist der Hauptkontrollfluß?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
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.
Damit ist der Code gemeint der auf Modulebene ausgeführt wird wenn das Modul importiert wird und der nur Konstanten, Funktionen, und Klassen definiert.
Mit tkinter kenne ich mich zu wenig aus, daher kann ich dazu nicht viel sagen.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.
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()
...
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)

-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
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.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
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
@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?
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Ja das möchte ich gerne von Dir wissen, denn Du bist es ja, der schrieb: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?
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.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.
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.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Genau, das habt ihr gesagt und weiter, dass man deshalb nichts mehr anfügen soll.Sirius3 hat geschrieben:Und was folgerst Du daraus? vehicle und vehicle.statusbus sind keine Konstanten.Sirius3 hat geschrieben:Und da wurde global soll man nicht machen und nachträglich ranfummeln soll man auch nichts, mehrfach geäußert.
Wenn ich eine Texteditor habe und Text schreibe, dann ist der Text auch keine Konstante, soll man den gleich lassen?
-
- 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?
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?