global oder nicht global

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.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

@snafu: Du hast beim zweiten `import` nicht das Modul nochmal importiert; die Zeile bewirkte nichts, weil sich das Modul noch im Cache befand. Mit `reload` wird es wirklich neu geladen und es gibt ein anderes Verhalten als du es dargestellt hast:

Code: Alles auswählen

>>> import funwithfoo
>>> funwithfoo.print_foo()
foo
>>> funwithfoo.set_foo('bar')
>>> funwithfoo.print_foo()
bar
>>> reload(funwithfoo)
<module 'funwithfoo' from 'funwithfoo.pyc'>
>>> funwithfoo.print_foo()
foo
gNeandr
User
Beiträge: 68
Registriert: Sonntag 11. Mai 2014, 16:48

:? .. so ein wenig raucht der Kopf.
Die Lektüre über Namespaces, descriptoren usw usw sind alle schön & gut, aber die grundsätzliche Methodik wie zB von BlackJack beschrieben
Eine allgemein gültige Handhabung dürfte sein statt globalen Zustand zu verwenden, den mit Argumenten/Parametern zu lokalem Zustand zu machen der herum gereicht wird.
kann ich noch nicht damit lösen (oder nur noch nicht erkennen?).

Da ich innerhalb eines Programms zwei/ggf. mehrere Threads haben werde, aber bestimmte Parameter in den Threads verwendet oder auch in diesen verändert werden müssen, war mein erster Gedanke diese als 'global' aufzusetzen. Und ja, ich sehe die Gefahr, dass deren Zustand nicht immer überschaubar ist (wer hat wann was geändert).
Und hat ev. ein Programm/Modul 'unbeabsichtigten' Schreib-Zugriff (Nameskonflikte).

Aus Javascript kenne ich Closures und letztlich ist das doch in Python auch möglich?!

Warum das "herumreichen" der Daten die bessere Lösung ist erschließt sich mir noch nicht, insbesondere mit den erforderlichen Threads .. siehe oben.
Sorry ...
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Und schon hast Du den nächsten Punkt getroffen, wann man niemals globale Variablen benutzen darf: Threads. Da Threads zu nicht kontrollierbaren Zeitpunkten globale Variablen ändern können, ist es auch unmöglich, Fehler, die dadurch entstehen zu finden, zu reproduzieren und zu beseitigen. Threads müssen in ihrer eigenen Welt leben und der Kontakt zu anderen Threads muß genau geplant werden (z.B. mit Queues).
gNeandr
User
Beiträge: 68
Registriert: Sonntag 11. Mai 2014, 16:48

Sirius3 hat geschrieben:Und schon hast Du den nächsten Punkt getroffen, ... (z.B. mit Queues).
Um des Himmels Willen ... was mach ich denn jetzt damit?????
BlackJack

@gNeandr: Closures sind in Python „vollwertig” erst ab 3.0 möglich weil es vorher noch kein ``nonlocal``-Schlüsselwort gibt, aber auch damit wäre es nicht „pythonisch”, denn der eine, offensichtliche Weg, sind nun mal Klassen. Python hat und hatte schon immer Klassen dafür, also wäre es komisch die mit Closures nachzubauen, als wenn es diese Möglichkeit nicht gäbe.

Ich verstehe ehrlich gesagt nicht so ganz wo das Problem liegt. Es gibt keine technische Lösung die irgendwie einen tollen Namen hat, oder die man irgendwie genauer beschreiben könnte als: Verwende einfach keinen globalen Zustand. Was doch in vielen Fällen auch überhaupt gar kein Problem ist. Wenn zwei Threads sich Zustand teilen, dann übergibt man den einfach beiden vor dem bzw. beim Start und schon hat man keinen globalen Zustand. Genau das habe ich doch zum Beispiel in dem anderen Thema mit dem „exit flag” gemacht.

Und das hat auch nichts mit Closures zu tun, denn auch bei Sprachen in denen *das* der bevorzugte Weg ist um Objekte zu erstellen, möchte man nach Möglichkeit keinen globalen Zustand damit modellieren, das heisst diese beiden Themen sind orthogonal.
gNeandr
User
Beiträge: 68
Registriert: Sonntag 11. Mai 2014, 16:48

@BlackJack
OK, mit dem Hinweis auf das andere Thema meinst du sicher diese Zeilen #50 .. 53:

Code: Alles auswählen

    exit_event = Event()
    Thread(target=print_time, args=(exit_event, 'myTimer', 2)).start()
    serve(exit_event, 'myListener')
und als 'Zustand' ist hier "exit_event" gemeint.
Soweit verstanden. Aber irgendwie sehe ich halt nicht den "Vorteil" Zustände (was ja nix anderes als Daten sind) rumzureichen. Das kann meiner Meinung nach auch recht unübersichtlich werden.

Mein Beispiel hier im Thread kapselt die als global benutzten Daten ja innerhalb 'def gParam():' und sind nur über 'gSet,gGet' ansprechbar. Mag sein, dass es nicht der reinen pythonischen Lehre entspricht, aber sollte besser sein als global Datennamen wie 'server'; dh. meine Lösung schützt diesen Wert durch gSet('server', '192.....') bzw gGet('server').
Und geht das nicht in Richtung pythonischer Wörterbuch Funktion .. wie ja auch vorgeschlagen?
BlackJack

@gNeandr: Wenn man einfach nachvollziehen kann welche Daten von einem Stück Code benutzt werden ohne dass man dazu das ganze Programm durchlesen muss, oder bei Daten einfach(er) sehen kann welcher Code die benutzt und verändern kann, ohne dass man das ganze Programm durchlesen muss, dann ist das IMHO immer ein Vorteil. Das Programm ist dadurch einfacher nachvollziehbar, testbar, wart- und erweiterbar. Aber das hatten wir doch alles schon.

Ob Du globale Daten nun kapselst, oder besser gesagt wie Du die kapselst, ist egal, es bleiben ja globale Daten, und die werden durch keine Art der Kapselung besser. Wie schon gesagt, die beiden Themen sind orthogonal. Ich sehe auch nicht wo Deine Lösung etwas ”schützt”, und wovor? Ob man nun ein Modul für die Konfiguration anlegt (``import config; config.server = '123...'; print config.server``), das in einem globalen Wörterbuch ablegt (``config['server'] = '123...'; print config['server']``), oder über ein Closure darauf zugreift (``g_set('server', '123...'); print g_get('server')``) ändert semantisch ja nichts daran, dass es globale Daten sind.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@gNeandr:
So war mein Hinweis oben auf Closures natürlich nicht gemeint. Selbst wenn Du Zustände in Dateien rausschreibst um sie später tief im Code vergraben wieder zu laden und zu manipulieren, bleibts beim globalen Charakter der Zustände und ist ein Hinweis auf einen schlechten Entwurf, der Dir irgendwann um die Ohren fliegen wird.

Mir scheint, dass Dir nicht klar ist, warum globale Zustände, egal in welcher Form sie daher kommen, zum Problem werden. Idee ist doch, die Ablaufsteuerung möglichst zentral zu orchestrieren. Diese zentrale "Anlaufstelle" ist zB. in C die main-Funktion. Python erzwingt diese nicht, dafür hat sich unter Python der `if __name__ == '__main__': Block etabliert. Von hier aus gibst Du Zuständigkeit an Funktionen oder Exemplare und deren Methoden ab (über Parameter). Innerhalb deren Implementation orchestrieren sie wiederum ihre Subaufrufe (sind eine Art lokales "main"). Das Scoping von Python (und der meisten anderen Sprachen ;) ) folgt im wesentlichen dieser Idee. Im Idealfall sind Exemplare und Funktionen vollständig gekapselt - egal wo und zu welcher Zeit Du ein Exemplar erstellst oder ein Ergebnis einer Funktion erhältst - sie verhalten sich gleich und sind damit vorhersagbar.

Globale/Semiglobale Zustände führen in dieses Modell das Problem der "Nebenabsprachen" ein. Übrigens sind auch Konstanten eine Art Nebenabsprache, weichen aber obiges Prinzip nicht auf, da sie konstant sind (erzwungen oder per Konvention). Auch gibt es mit den Objektattributen eine gewollte Abweichung von diesem Prinzip ("objektglobal" - sind objektweit manipulierbar). Dadurch wird es möglich, Objekte imperativ zum Leben zu erwecken.

Da Du von Javascript sprachst - auch in der ereignisbasierten Programmierung sollte man kapseln (gut geschriebene JQuery-Plugins zeigen das ganz gut). Man steigt dort idR nicht mit "main" ein, da die Rahmenwerke bzw. der Browser diese Funktion mit der Ereignisschleife besetzen.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

derdon hat geschrieben:@snafu: Du hast beim zweiten `import` nicht das Modul nochmal importiert; die Zeile bewirkte nichts, weil sich das Modul noch im Cache befand. Mit `reload` wird es wirklich neu geladen und es gibt ein anderes Verhalten als du es dargestellt hast:
Ich sprach schon ganz bewusst vom ``import``-Statement und dem dafür typischen Verhalten. Wenn ich das selbe Modul in mehreren Untermodulen verwende, dann mache ich dies nicht mit einem ``reload``, sondern eben mit ``import``-Statements.
gNeandr
User
Beiträge: 68
Registriert: Sonntag 11. Mai 2014, 16:48

@jerch
in C die main-Funktion. Python erzwingt diese nicht, dafür hat sich unter Python der `if __name__ == '__main__': Block etabliert. Von hier aus gibst Du Zuständigkeit an Funktionen oder Exemplare und deren Methoden ab (über Parameter). Innerhalb deren Implementation orchestrieren sie wiederum ihre Subaufrufe (sind eine Art lokales "main"). Das Scoping von Python (und der meisten anderen Sprachen ;) ) folgt im wesentlichen dieser Idee. Im Idealfall sind Exemplare und Funktionen vollständig gekapselt - egal wo und zu welcher Zeit Du ein Exemplar erstellst oder ein Ergebnis einer Funktion erhältst - sie verhalten sich gleich und sind damit vorhersagbar.

Dieses Konstruktion würde ich mir gerne näher ansehen, kannst du mir ein Beispiel geben .. Link ..

:D Danke
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

snafu hat geschrieben:
Darii hat geschrieben:global ist der "last resort". Genau wie goto in C.
Dafür, dass ``goto`` ein "last resort" sein soll, wird es in echten Programmen - u.a. im C-Quelltext von Python und im Linux-Kernel - aber ganz schön oft verwendet. Wie passt das mit deiner Aussage zusammen? Sind das für dich allesamt schlechte Programmierer oder setzt du eine sehr spezielle Definition von "last resort" voraus?
Meine Definition "last resort = letzter Ausweg" (laut Wörterbuch). Dass dieser letzte Ausweg trotzdem oft genommen wird liegt einfach daran, dass C keine äquivalenten besseren Sprachmittel z.B. zur Fehlerbehandlung anbietet.

Sprich goto sollte man in C genau wie global in Python nur verwenden, wenn es gar nicht anders (oder nicht besser) geht. Kommt in C leider nunmal recht häufig vor…
BlackJack

@Darii: ``goto`` ist nicht letzter Ausweg, sondern das Mittel der Wahl um die Sprünge, die in anderen Sprachen per Ausnahmebehandlung geregelt werden, in C zumindest ansatzweise umzusetzen. Letzter Ausweg ist etwas was man nimmt wenn man verzweifelt ist. Es mag sicher Leute geben die beim C-Programmierung ständig am verzweifeln sind, aber das ist nicht der Masstab an dem man das festmachen sollte. ;-)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@gNeandr: Hier mal ein simples Beispiel, einmal mit globaler Variable und einmal mit Parameter. Wenn Du mir die -1 für a global erklären kannst, hast Du gut aufgepasst - dann ist aber interessant, was nötig war, um das nachzuvollziehen. In real world code würde ich sowas immer als Programmierfehler ansehen. Beachte bitte auch, wie sich der Generator für a als Parameter immer gleich verhält.

Code: Alles auswählen

def gen():
    global a
    while a > 0:
        yield a
        a -= 1

print 'a as global'
a = 10
for i in gen():
    print i, a, list(gen())
print a, list(gen())

#################################

def reentrant_gen(a):
    while a > 0:
        yield a
        a -= 1

print 'a as parameter'
a = 10
for i in reentrant_gen(a):
    print i, a, list(reentrant_gen(a))
print a, list(reentrant_gen(a))
gNeandr
User
Beiträge: 68
Registriert: Sonntag 11. Mai 2014, 16:48

@jerch ... besonders spannend finde ich das jetzt nicht, sorry.
Vielleicht liegt es daran, dass ich bestimmte "Werte" sehe, die ich an verschiedenen Stellen im Programm benutzen muss,
a) feste "Werte" -- zB 'server', die IP die ich praktischerweise beim Start einlese und in mehreren Funktionen / Threads verwenden muss,
oder
b) "Werte" die ich nach Berechnung (von importierten Ausgangswerten) weiter 'global' verfügbar halte (zB. Geo-Koordinaten zur Berechnung von 'sunrise' und 'sunset')

In beiden Fällen werden die einmal "ermittelt" und warten dann nur noch verwendet zu werden. Also recht einfach ..'global'.

Sicher gibt's Fälle in denen das "rumreichen" von Zuständen Sinn macht. BlackJack hat ja dankenswerterweise meinen ersten Entwurf entsprechend verbessert. Und Jerchs Beispiel zeigt das ja auch.

Nix für Ungut, eure Hinweise helfen und eines Tages werde ich wohl pythonischer denken ... :wink:
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@gNeandr: Das hat doch nichts mit pythonisch/unpythonisch zu tun, in C, C++ oder Java würde man das auch so machen. Und spätenstens bei Threads handelst Du Dir mit globalen Variablen entweder sowas wie Schrödingers Katze oder ein segfault ein, wenn da zeitgleich dranrum manipuliert wird. :roll:
BlackJack

@gNeandr: Das hat nichts mit „pythonisch” zu tun, das ist allgemein gültig. Es gibt nicht bestimmte Fälle in denen das vermeiden von globalen Variablen Sinn macht, sondern das macht generell Sinn. Das was Du da jetzt beschrieben hast (Server, feste Koordinaten) sind ja auch keine Variablen sondern Konstanten, also braucht man da auch keine ``global``-Anweisung. Wenn man die ”braucht” ist das ein „code smell”, und IMHO in 99,9% der Fälle ein Entwurfsfehler.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

BlackJack hat geschrieben:@Darii: ``goto`` ist nicht letzter Ausweg, sondern das Mittel der Wahl um die Sprünge, die in anderen Sprachen per Ausnahmebehandlung geregelt werden, in C zumindest ansatzweise umzusetzen.
Der einzige Ausweg ist zwangsläufig auch der letzte. ;) Fakt ist, dass man bei goto sehr leicht übersehen kann, irgendwelche Ressourcen freizugeben, was für mich eigentlich ein Grund ist, es nicht benutzen zu wollen…
Es mag sicher Leute geben die beim C-Programmierung ständig am verzweifeln sind, aber das ist nicht der Masstab an dem man das festmachen sollte. ;-)
Ich wage einfach mal die Behauptung, dass jeder, der bei C nicht verzweifelt, C nicht benutzen sollte. Ich hoffe ja, dass nach heartbeat & co endlich mal ein paar Leute aufgewacht sind…
BlackJack

@Darii: Das ist eine unsinnige Argumentation weil dann *alles* ein letzter Ausweg ist. Man kann auch ohne ``goto`` leicht übersehen Ressourcen freizugeben, das ist eben so wenn man sich selbst um die Verwaltung kümmern muss. Es macht, richtig eingesetzt, den Quelltext übersichtlicher, was wiederum dazu führt, das eben nicht so schnell etwas übersehen wird als bei unübersichtlichem Quelltext ohne das ``goto``.

Wenn dann effektiv niemand C benutzen sollte, fehlt irgendwie eine Alternative. Ich will ja nicht behaupten das C der Weisheit letzter Schluss ist, sehe aber nicht wodurch man es ersetzen sollte. Also ganz praktisch gesehen, nicht was theoretisch besser wäre.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Darii hat geschrieben:Ich wage einfach mal die Behauptung, dass jeder, der bei C nicht verzweifelt, C nicht benutzen sollte.
Haha, in der Tat kenne ich den Frust mit C - allerdings eher daher, dass mir Sprachmittel fehlen und man soviel "zu Fuss" erledigen muss. Aber Verzweiflung ist so ein starkes Wort :wink:

Und wenn man sich heartbleed anschaut - nunja, da sind schon grobe Schnitzer drin gewesen. Das man in C IMMER die Größe von Pointern auf Arrays/Strings (oder allgemeiner Speicherbereiche) checken muss, ist hinlänglich bekannt. Traurig daran ist, dass das durch die angeblich zweistufige QA gekommen ist.

Die richtige Benutzung von GOTO steht und fällt mit der eigenen Codedisziplin und da es "nur" innerhalb eines Funktionsblockes erlaubt ist, ist die Tragweite durchaus überschaubar (im Sinne von Codezeilen). Aber auch ich bin kein Fan von GOTO.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Wenn dann effektiv niemand C benutzen sollte, fehlt irgendwie eine Alternative. Ich will ja nicht behaupten das C der Weisheit letzter Schluss ist, sehe aber nicht wodurch man es ersetzen sollte. Also ganz praktisch gesehen, nicht was theoretisch besser wäre.
Zugegeben fertige Alternativen gibt es noch keine, aber ich setze große Hoffnungen in Rust. Die Sprache ist zwar noch nicht fertig, aber die freuen sich über jeden Bugreport und das ist besser als den Kopf in den Sand zu stecken. Ich habe hoffentlich bald mal Zeit Rust bare metal auf einem Microcontroller auszuprobieren…
jerch hat geschrieben:Und wenn man sich heartbleed anschaut - nunja, da sind schon grobe Schnitzer drin gewesen. Das man in C IMMER die Größe von Pointern auf Arrays/Strings (oder allgemeiner Speicherbereiche) checken muss, ist hinlänglich bekannt. Traurig daran ist, dass das durch die angeblich zweistufige QA gekommen ist.
Wenn man in C *IMMER* die Größe von Speichersegmenten checken muss warum macht die Sprache das nicht für mich? Das würde eine komplette Klasse von Fehlern ausschließen. Die Lehre aus Heartbleed sollte eben sein, dass man solch gravierende Sprachdefizite nicht durch QA oder (reklamierte) Fertigkeiten des Programmierers kompensieren kann.
Antworten