Was ist eine globale Variable und warum soll man sie nicht benützen?
- pillmuncher
- User
- Beiträge: 1482
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
@Alfons Mittelmeyer: Schau dir mal diesen Vortrag von Brandon Rhodes an: The Clean Architecture in Python
In specifications, Murphy's Law supersedes Ohm's.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Was man nicht tun sollte, ist, ein Dogma aus etwas zu machen, das für einen ganz bestimmten Zusammenhang gemeint war.__deets__ hat geschrieben:Die Tatsache das man etwas nicht tun sollte impliziert nicht, dass man es nicht tun darf. Man sollte es nur unter Abwaegung der Alternativen tun.
Und der Zusammenhang ist, dass viele Anfänger nicht OOP programmieren und statt Klassen und statt Aufräumen eine Unzahl vom Modulinternen Variablen haben auf die sie zugreifen. Klar, dass man das nicht haben soll, sondern sauber aufgeräumt und gekapselt und sinnvoll gegliedert implementieren sollte.
Wie kommst Du auf diese komische Metapher mit dem scharfen Gegenstand?__deets__ hat geschrieben:Es ist allgemein eine schlechte Idee, mit einem scharfen Gegenstand die Bauchhoehle zu eroeffnen. Das bedeutet aber deshalb nicht, dass man Chirurgie verbietet. Man sollte aber zum befestigen einer Hose einen Guertel verwenden, statt sie mit Darmschlingen zu verknoten.
Allles ist möglich, man kann es es wahrscheinlich immer vermeiden, auch wenn man dann manchmal statt etwas Einfachem eine häßliche fehleranfällige Programmierung zu machen hat.__deets__ hat geschrieben:Globaler Zustand ist allgemein der Anus Praeter der Programmierung. Man will das nicht, es ist haesslich und macht das Leben schwerer, aber man kann's halt nicht immer vermeiden.
Bei diesem Beispiel: http://code.activestate.com/recipes/413 ... ython-way/
ist in Zeile 26 ein globales Objekt definiert:
features = FeatureBroker()
Warum wird das in diesem Forum so gesehen, als ob das ein Verbrechen ist oder jedenfalls als etwas, was es auf Teufel komm raus zu vermeiden gilt?
Das wird nicht nur so in diesem Forum so gesehen, sondern von jedem, der gut programmieren kann.
Und wann kommt mal der Moment an dem du begreifst, das ein Gegenbeispiel fuer eine *Richtlinie* nicht die gleichen Konsequenzen hat wie fuer einen mathematischen Beweis?
Wenn du hier Code schreibst, der unnoetigerweise global verwendet oder globalen Zustand zur Funktion vorraussetzt, bleibt das schlechter Code, und das wird dir dann auch weiterhin so gesagt werden. Die Existenz von anderem schlechten Code, oder Code der aufgrund bestimmter Randbedingungen so geschrieben werden musste aendert daran nix.
Und wann kommt mal der Moment an dem du begreifst, das ein Gegenbeispiel fuer eine *Richtlinie* nicht die gleichen Konsequenzen hat wie fuer einen mathematischen Beweis?
Wenn du hier Code schreibst, der unnoetigerweise global verwendet oder globalen Zustand zur Funktion vorraussetzt, bleibt das schlechter Code, und das wird dir dann auch weiterhin so gesagt werden. Die Existenz von anderem schlechten Code, oder Code der aufgrund bestimmter Randbedingungen so geschrieben werden musste aendert daran nix.
- pillmuncher
- User
- Beiträge: 1482
- Registriert: Samstag 21. März 2009, 22:59
- Wohnort: Pfaffenwinkel
Es ist doch ganz einfach. Ein gutes Programm sieht typischerweise so aus:
Ein weniger gutes typischerweise so:
Ersteres zeigt das Innenleben eines VIntage Hiwatt Amps (ich hab zwei davon), zweiteres das eines Vintage Fender Amps. Globaler Zustand führt typischerweise zu Programm-Strukturen, die aussehen wie das zweite Bild, das Vermeiden von globalem Zustand zu solchen, die aussehen wie das erste.
Ein weniger gutes typischerweise so:
Ersteres zeigt das Innenleben eines VIntage Hiwatt Amps (ich hab zwei davon), zweiteres das eines Vintage Fender Amps. Globaler Zustand führt typischerweise zu Programm-Strukturen, die aussehen wie das zweite Bild, das Vermeiden von globalem Zustand zu solchen, die aussehen wie das erste.
In specifications, Murphy's Law supersedes Ohm's.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Genau das ist der Zweck, warum man manchmal dasselbe Objekt global verfügbar machen sollte, anstatt es überall durchzureichen.pillmuncher hat geschrieben:Es ist doch ganz einfach. Ein gutes Programm sieht typischerweise so aus
Ein System, besteht aus diversen Subsystemen. Bei der Initialisierung mancher Subsysteme wird zuerst ein Objekt instanziert, das noch weiter durch diverse Daten konfiguriert wird. Um mit diesen Subsystemen zu arbeiten, braucht man dann diese konfigurierten Objekte.
Doch was tun, wenn es heißt, daß man keine globalen Objekte haben soll, sonder nur Funktionen und Klassen?
Ich habe jetzt die Lösung gefunden.
Code: Alles auswählen
# statt
my_object = _MyClass()
# und dann globalen Zugriff auf my_object, laesst sich das auch mit einem Singleton loesen:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
# python3
class MyClass(metaclass=Singleton):
def __init__(self):
self.number = 0
# test ----
a = MyClass()
a.number = 123
b = MyClass()
print(b.number)
@Alfons Mittelmeyer: Du findest immer wieder andere Varianten, globale Variablen zu definieren. Es sind und bleiben aber globale Variablen. Singletons werden in Python nicht gebraucht, weil man ja prinzipiell globale Variablen definieren kann - für die wenigen Fälle, wo das wirklich sinnvoll ist. Singletons wurden für Sprachen erfunden, wo es keine globalen Variablen gibt, sich die Leute aber dennoch nicht davon abbringen ließen, welche zu benutzen.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Ja, und wenn für die wenigen Fälle, wo man es braucht, es immer wieder heißt, globale Variablen soll man nicht nehmen, dann bleiben nur Singletons übrig.Sirius3 hat geschrieben:Singletons werden in Python nicht gebraucht, weil man ja prinzipiell globale Variablen definieren kann - für die wenigen Fälle, wo das wirklich sinnvoll ist.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Ok, das wollte ich wissen.Sirius3 hat geschrieben:@Alfons Mittelmeyer: hallo, jemand zu Hause? Singletons sind globale Variablen. Wenn man globale Variablen braucht sollte man globale Variablen benutzen. Wenn man sie nicht unbedingt braucht, dann nicht.
@Alfons Mittelmeyer:
Oder so:Damit ist fast alles über `self` erreichbar (mit etwas fummeln bekommt man auch die imports da dran).
Oder so:
Code: Alles auswählen
class World(object):
import a
def funcXY(self):
...
...
def main(self):
...
World.main()
Hallo pillmuncher
viewtopic.php?p=308156#p308156
Danke und Gratulation für deinen Superbeitrag der mich vom gähnen erlöst hat. Übrigens auch das zweite Bild ist noch recht gut überblickbar!
Gruss wuf
viewtopic.php?p=308156#p308156
Danke und Gratulation für deinen Superbeitrag der mich vom gähnen erlöst hat. Übrigens auch das zweite Bild ist noch recht gut überblickbar!
Gruss wuf
Take it easy Mates!
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
@jerch: ja nichtglobal zu importieren, geht natürlich auch
@jerch und Alfons Mittelmeyer: nein, nicht-global importieren geht gar nicht. Importe sind immer global. und wie wir schon ein paar Beiträge vorher gehört haben, sollte sobald das Programm mit der eigentlichen Arbeit anfängt, alle globalen Definitionen abgeschlossen sein, dazu gehören auch Importe. Zudem werden die Abhängigkeiten klar, wenn alle Importe am Anfang der Datei gefunden werden.
Und jetzt kommt wieder jemand mit Lazy- bzw. dynamischem Laden. Sowas sollte man sich gut Überlegen, ob man das wirklich braucht. Das macht Programme deutlich schwieriger zu Testen und erhöht die Komplexität nicht unerheblich. Das ist im Normalfall also nicht nötig oder empfehlenswert.
Und jetzt kommt wieder jemand mit Lazy- bzw. dynamischem Laden. Sowas sollte man sich gut Überlegen, ob man das wirklich braucht. Das macht Programme deutlich schwieriger zu Testen und erhöht die Komplexität nicht unerheblich. Das ist im Normalfall also nicht nötig oder empfehlenswert.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Nein damit komme ich bestimmt nicht, beim dynamischen Laden statt import, würde ja die Variable neu kreiert und wäre damit eine neue Instanz.Sirius3 hat geschrieben:Und jetzt kommt wieder jemand mit Lazy- bzw. dynamischem Laden
Das wäre vergleichbar damit:
Code: Alles auswählen
def getvar():
return [5]
def main():
a = getvar()
print(a)
b = getvar()
a[0] = 7
b[0] = 6
print(a,b)
main()
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Also interessant sind solche dynamisch geladenen Scripte schon. Also wenn man sie lädt, dann sind alle Variablen nur lokal in der Ladefunktion vorhanden. Nach dem Laden hat man also nichts, worauf man zugreifen kann. Es sei denn, man schreibt die Ladefunktion so, dass sie ein ganz bestimmtes Objekt zurückgibt. Das ist natürlich nichts Globales, weil jeder Aufruf ein neues Objekt erzeugt.jerch hat geschrieben:Also ich fand die Idee gut, so konzeptionös...
Interessant ist auch folgendes Verhalten, z.B:
Code: Alles auswählen
import tkinter as tk
print(tk.BOTTOM)
def test():
print(tk.TOP)
test()
So funktioniert print(tk.BOTTOM), weil es außerhalb einer Funktion aufgerufen wurde. Aber das print(tk.TOP) funktioniert nicht, da die Variablen außen nicht innen bekannt sind. Was außen ist, ist also auch lokal, Funktionen können keine anderen Funktionen aufrufen, da die auch nicht bekannt sind, es sei denn, man übergibt sie als Parameter.
Wenn man innerhalb einer Klasse, auf etwas außerhalb zugreifen will, muss es als Parameter übergeben werden, auch das tk für tkinter!
Es besteht also hier ein äußerst gekapseltes Verhalten, strenger als sonst, obwohl Code vielleicht so aussieht, als hätte man globale Variablen.
Natürlich funktioniert dies, wie jeder leicht überprüfen kann. Dies entspricht auch der LGB-Regel — also keine Überraschung.Alfons Mittelmeyer hat geschrieben:So funktioniert print(tk.BOTTOM), weil es außerhalb einer Funktion aufgerufen wurde. Aber das print(tk.TOP) funktioniert nicht, da die Variablen außen nicht innen bekannt sind.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Dann teste mal, was da geht. Das wäre das, was geladen wird und soll heißen: test.pykbr hat geschrieben:Natürlich funktioniert dies, wie jeder leicht überprüfen kann. Dies entspricht auch der LGB-Regel — also keine Überraschung.Alfons Mittelmeyer hat geschrieben:So funktioniert print(tk.BOTTOM), weil es außerhalb einer Funktion aufgerufen wurde. Aber das print(tk.TOP) funktioniert nicht, da die Variablen außen nicht innen bekannt sind.
Code: Alles auswählen
import tkinter as tk
print(tk.BOTTOM)
def test():
print(tk.TOP)
test()
Code: Alles auswählen
def script_exec(filename):
exec(compile(open(filename, "r").read(), filename, 'exec'))
script_exec('test.py')
Das ist für python3. Probiere es aus, ich weiß wovon ich rede.
Und das funktioniert dann:
Code: Alles auswählen
import tkinter as tk
print(tk.BOTTOM)
def test(tk):
print(tk.TOP)
test(tk)
Code: Alles auswählen
import tkinter as tk
print(tk.BOTTOM)
def test(tk):
print(tk.TOP)
test(tk)
returnwert = tk.RIGHT
Code: Alles auswählen
def script_exec(filename):
exec(compile(open(filename, "r").read(), filename, 'exec'))
return locals()['returnwert']
print(script_exec('test.py'))
Wenn `exec()` nicht sowieso schon etwas wäre was man nicht benutzen sollte wenn es da keinen richtig guten Grund gibt, dann schon gar nicht so das dieses überraschende Verhalten entsteht. Das muss man entweder *sehr* deutlich dokumentieren, oder die `script_exec()`-Funktion so schreiben das sich die Ausführung nicht so unerwartet verhält.