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.
Benutzeravatar
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.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__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.
Was man nicht tun sollte, ist, ein Dogma aus etwas zu machen, das für einen ganz bestimmten Zusammenhang gemeint war.

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.
__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.
Wie kommst Du auf diese komische Metapher mit dem scharfen Gegenstand?
__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.
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.

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

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.
Benutzeravatar
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:

Bild

Ein weniger gutes typischerweise so:

Bild

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.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

pillmuncher hat geschrieben:Es ist doch ganz einfach. Ein gutes Programm sieht typischerweise so aus
Genau das ist der Zweck, warum man manchmal dasselbe Objekt global verfügbar machen sollte, anstatt es überall durchzureichen.

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)
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@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.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

pillmuncher hat geschrieben:Ein weniger gutes typischerweise so
Sensationell :D
Und wenn schon Singletons, dann Borgs.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

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.
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
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@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
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

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.
Ok, das wollte ich wissen.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Alfons Mittelmeyer:
Oder so:

Code: Alles auswählen

class World(object):
    import a
    def funcXY(self):
        ...
    ...
    def main(self):
        ...

World.main()
Damit ist fast alles über `self` erreichbar (mit etwas fummeln bekommt man auch die imports da dran). :lol:
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

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 :wink:
Take it easy Mates!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@jerch: ja nichtglobal zu importieren, geht natürlich auch
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@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.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:Und jetzt kommt wieder jemand mit Lazy- bzw. dynamischem Laden
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.

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()
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Also ich fand die Idee gut, so konzeptionös...
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jerch hat geschrieben:Also ich fand die Idee gut, so konzeptionös...
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.

Interessant ist auch folgendes Verhalten, z.B:

Code: Alles auswählen

import tkinter as tk

print(tk.BOTTOM)

def test():
    print(tk.TOP)

test()
Das war ein Test, ob sich in solch einem Script etwas Importiertes genauso verhält wie Variablen. Ja so ist es. Also die Variablen des Scripts sind script lokal, weil sie erstens nicht an die Ebene weitergegeben werden, welche die Ladefunktion aufruft. Zweiten gibt es da noch ein ungewohntes Verhalten. Das was außerhalb von Funktionen steht und man als global geneigt ist anzusehen, ist nämlich auch nicht modul global. Man kann auf solche Variablen auf der Scriptebene außerhalb von Definitionen zugreifen. Das gilt auch für etwas Importiertes.

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.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

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.
Natürlich funktioniert dies, wie jeder leicht überprüfen kann. Dies entspricht auch der LGB-Regel — also keine Überraschung.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

kbr hat geschrieben:
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.
Natürlich funktioniert dies, wie jeder leicht überprüfen kann. Dies entspricht auch der LGB-Regel — also keine Überraschung.
Dann teste mal, was da geht. Das wäre das, was geladen wird und soll heißen: test.py

Code: Alles auswählen

import tkinter as tk

print(tk.BOTTOM)

def test():
    print(tk.TOP)

test()
Und das ist das Main Script, welches dieses File lädt:

Code: Alles auswählen

def script_exec(filename):
    exec(compile(open(filename, "r").read(), filename, 'exec'))

script_exec('test.py')
Da erscheint die Fehlermeldung: NameError: name 'tk' is not defined

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)
Hier gibt es nichts Globales, ganz besonders nicht für das Main Script oder ein sonstiges Modul, welches das aufruft. Man kann aber einen returnwert zurückgeben, etwa:

Code: Alles auswählen

import tkinter as tk

print(tk.BOTTOM)

def test(tk):
    print(tk.TOP)

test(tk)

returnwert = tk.RIGHT
Und in diesem Zusammenhang:

Code: Alles auswählen

def script_exec(filename):
    exec(compile(open(filename, "r").read(), filename, 'exec'))
    return locals()['returnwert']

print(script_exec('test.py'))
BlackJack

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.
Antworten