Gültigkeitsbereich von Variablen

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.
jupa
User
Beiträge: 4
Registriert: Freitag 7. Juni 2024, 13:36

Hallo,

als absoluter Python- (und Forums-)Neuling kämpfe ich mich derzeit durch verschiedene Einsteiger-Tutorials. Konnte aber noch nicht herausfinden, warum der folgende Code nicht funktioniert wie ich erwarte:

Code: Alles auswählen


a = 10   # Der Variablen a wird der Wert 10 zugewiesen
b = 15   # Der Variablen b wird der Wert 15 zugewiesen
c = a + b    # Der Variablen c wird der Wert a+b  (= 25) zugewiesen
print(c)     # Test ok, 25 wird ausgegeben
a = 20    # Variable a wird überschrieben/geändert auf den Wert 20.
print(a)  # Test ok, a ist 20
print(c)  # Hier würde ich die Ausgabe der Summe von a und b (= 35) erwarten, erhalte aber noch immer 25.


# Was mache ich falsch oder was muß ich ändern?

TIA
Benutzeravatar
pillmuncher
User
Beiträge: 1527
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@jupa:

Code: Alles auswählen

c = a + b
Das bedeutet nicht: berechne jedesmal, wenn c referenziert wird, die Summe von a und b, sondern: berechne die Summe von a und b zum jetzigen Zeitpunkt und weise das Ergebis der Variable c zu.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jupa: Ergänzend: Im Kommentar steht es ja richtig, das dort der *Wert* von ``a + b`` (=25) zugewiesen wird, nicht die Berechnungsvorschrift. Namen werden Werte zugewiesen, da wird keinerlei Information darüber gespeichert wie dieser Wert zustande gekommen ist.

Da käme man auch ziemlich schnell in Schwierigkeiten weil nicht jeder Ausdruck ein so einfach ”wiederholbares” Ergebnis hat. Zum Beispiel wenn `b` am Anfang nicht 15 sondern das Ergebnis von ``int(input("Bitte gib eine ganze Zahl ein: "))`` zugewiesen wurde. Was sollte denn dann passieren? Sollte da jedes mal wenn der Wert von `b` verwendet wird, direkt oder indirekt, der Benutzer erneut gefragt werden? Was ist mit Zufallszahlen (`random`-Modul)? Oder wenn die Werte Messergebnisse von einem Sensor sind?

Was man machen kann ist sich einen Datentyp zu schreiben der berechnete Attribute („properties“) hat, die jedes mal bei der Abfrage neu berechnet werden. Ein Beispiel mit extrem sinnlosen generischen Namen:

Code: Alles auswählen

class Class:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def c(self):
        return self.a + self.b


instance = Class(10, 15)
print(instance.c)  # 25
instance.a = 20
print(instance.c)  # 35
Da wären wir aber schon beim Thema „Klassen“. Wo mindestens noch das Thema „Funktionen“ dazwischen liegt.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
jupa
User
Beiträge: 4
Registriert: Freitag 7. Juni 2024, 13:36

@pillmuncher
Danke für die rasche Antwort, so wirklich glücklich bin ich damit aber nicht.
Das von mir erwartete Ergebnis kann natürlich erreicht werden, wenn ich c in einer zusätzlichen Codezeile erneut berechne (Zeile 7):

a = 10 # Der Variablen a wird der Wert 10 zugewiesen
b = 15 # Der Variablen b wird der Wert 15 zugewiesen
c = a + b # Der Variablen c wird der Wert a+b (= 25) zugewiesen
print(c) # ok, 25 wird ausgegeben
a = 20 # Variable a wird überschrieben/geändert auf den Wert 20.
print(a) # ok, a ist 20
c = a + b # nach Einfügen dieser Zeile hat c jetzt den erwarteten Wert 35.
print(c) # wie der nachfolgende print Befehl zeigt.

Das würde aber bedeuten, daß jedes mal, wenn sich in einem Ausdruck (hier c = ... ) eine oder mehrere der Eingangsvariablen (hier a und b) ändert, die resultierende Variable (c) ausdrücklich nochmals neu berechnet werden muß. Für komplexere und verschachtelte Strukturen (c seinerseits wird wieder in einem anderen Ausdruck verwendet usw.) wäre das nicht nur ziemlich aufwendig, sondern auch unübersichtlich und fehleranfällig. Denke ich hier völlig falsch oder geht das doch irgendwie, daß der Wert einer berechneten Variable (c) automatisch aktualisiert wird, wenn sich die Eingangsdaten (hier a und b) ändern?
jupa
User
Beiträge: 4
Registriert: Freitag 7. Juni 2024, 13:36

__blackjack__ hat geschrieben: Freitag 7. Juni 2024, 17:10 Im Kommentar steht es ja richtig, das dort der *Wert* von ``a + b`` (=25) zugewiesen wird, nicht die Berechnungsvorschrift. Namen werden Werte zugewiesen, da wird keinerlei Information darüber gespeichert wie dieser Wert zustande gekommen ist.
Danke für diese Ergänzung, das erklärt natürlich alles. (Ich war mir des Unterschiedes zwischen "Wertzuweisung" und "Zuweisung der Berechnungsvorschrift" nicht bewußt).
Da ich noch ganz am Anfang stehe (meine Lernkurve ist ziemlich flach) wird es noch ein Weilchen dauern, bis ich mich mit Klassen &Co. beschäftige.
Aber ich fühle mich hier im Forum gut aufgehoben und werde bestimmt noch öfter "nerven" (müssen).
Vorab schon mal Vielen Dank!
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jupa: Unübersichtlich und fehleranfällig wäre das von Dir gewünschte Verhalten. Wenn man `a` etwas zuweist, das sich dann `c` automagisch ändert, wäre bei den paar Zeilen hier noch nachvollziehbar — bei komplexeren und verschachtelteren Strukturen wäre das sehr überraschend das sich die Werte von einfachen Namen einfach so ändern, ohne das man da eine Zuweisung für im Code sieht.

Wenn sich der Wert von `c` ändern soll, dann muss man da einen neuen Wert zuweisen. Das ist einfach und nachvollziehbar. Man sieht die Zuweisung an `c` im Code. Wenn man dagegen `a` etwas zuweist, und sich daraufhin `c` ändert, und auch noch alles was dann seinerseits mal irgendwann mit `c` berechnet wurde irgendwo im einem mehrere hundert Zeilen langen Programm, da ist die Übersicht ganz schnell weg.

Und ich hatte in meinem letzten Beitrag schon Probleme angesprochen, dass das gar nicht in allen Fällen möglich ist, oder zumindest nicht klar ist in welchen Fällen man dieses Verhalten haben möchte und in welchen nicht. Da müsste auch eine ziemlich komplexe Maschinerie hinter stecken, die bei jeder Zuweisung immer alles aktualisiert was in der Vergangenheit mal mit einem Namen gemacht wurde. Auch wenn man das gar nicht braucht, oder auch gar nicht haben will, weil man ja auch mal feste Ergebnisse möchte, die sich nicht ändern. Weil, wie schon gesagt, das ist fehleranfällig.

Das funktioniert in den meisten Programmiersprachen so wie in Python. Was Du willst wird manchmal in Bibliotheken mit eigenen Datentypen gemacht, wo man dann solche Variablen speziell kennzeichnen/deklarieren muss. Aber das ist nicht der Normalfall. Das hier geht so in die Richtung: https://github.com/millejoh/pycells und https://pypi.org/project/promised/

Was ist denn das konkrete Problem was Du damit lösen wollen würdest?
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
nezzcarth
User
Beiträge: 1733
Registriert: Samstag 16. April 2011, 12:47

jupa hat geschrieben: Freitag 7. Juni 2024, 18:06 Danke für diese Ergänzung, das erklärt natürlich alles. (Ich war mir des Unterschiedes zwischen "Wertzuweisung" und "Zuweisung der Berechnungsvorschrift" nicht bewußt).
Ich glaube, es wird vielleicht noch mal klarer, wenn du dich mit Funktionen in Python befassen wirst; "Berechnungsvorschriften", die man an einer "Variable zuweist" [*] werden in Python durch Funktionen abgebildet. Die muss man aber auch neu Aufrufen und das Ergebnis neu "zuweisen".


[*] In Python gibt es eigentlich keine Variablen im engeren Sinne denen irgendwas zugewiesen wird sondern nur Objekte, die an Namen gebunden werden; der Einfachheit halber wird diese Terminologie umgangssprachlich so verwendet, aber beim Lernen kann es Sinn ergeben, sich zu verdeutlichen, dass das konzeptuell etwas anderes.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@nezzcarth: Zum [*]: Variablen ist halt Definitionssache, denn Variablen in Python funktionieren anders als Variablen in Pascal, und die in Haskell funktionieren noch mal anders. Eigentlich sind die in Haskell die ”richtigen”: Man kann dort *einmal* etwas zuweisen und das ist dann *fest*.

Zwischen Python und Pascal ist der Unterschied ob die Speicheradresse des Wertes zum Namen oder zum Wert gehört. Wenn die Speicheradresse zum Namen gehört, wie in Pascal, dann ist das aber keine Variable in einem engeren Sinn als wenn die Speicheradresse zum Wert gehört, wie in Python.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Benutzeravatar
snafu
User
Beiträge: 6830
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

ist halt wie ne Matheaufgabe. Wenn ich ``c = a + b`` ins Heft schreibe, dann ändert sich das Ergebnis auch nicht plötzlich, sobald ich 3 Zeilen weiter unten a und/oder b neu definiere. Wer die Aufgabe im Sinne einer Definition meint, der sucht eigentlich eine Funktion wie add(a, b).
Benutzeravatar
ThomasL
User
Beiträge: 1377
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Hallo jupa,

kann es sein, dass du viel mit Tabellenkalkulation z.B. Excel arbeitest?
Wenn man dort in die Zelle A3 den Ausdruck = A1+A2 ( äquivalent zu SUMME(A1,A2) ) reinschreibt und dann die Zellen A1 oder A2 verändert, wird automatisch der Wert in A3 neu errechnet.

Es wurde ja schon oben erklärt dass es in Python anders ist.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

In dem Fall ist https://pyspread.gitlab.io/ vielleicht interessant. Eine Tabellenkalkulation in der man Python in den Zellen verwenden kann.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
nezzcarth
User
Beiträge: 1733
Registriert: Samstag 16. April 2011, 12:47

@__blackjack__: Was ist denn an der Variable in Haskell noch variabel, wenn die nicht mehr veränderlich ist? ;)

Ich stimme zu, dass das Definitionssache ist. Die Frage ist finde ich halt, was die Vorteile davon sind, die verschiedenen Mechanismen, die du nennst in einem Begriff zusammen zu fassen oder eben nicht. Meiner Meinung nach macht es jedenfalls Sinn, zumindest im Lernkontext die Begrifflichkeiten zu trennen, wenn man verstehen möchte, wie das intern funktioniert. Soweit ich das überlicke, verweist Variable klassischer Weise halt auf die "Pascal-Variablen" die so veranschaulicht, werden dass man da irgendwelche beschrifteten Kästchen hat, in die man Werte legen und auch wieder rausnehmen kann. Und in Python ist das halt nicht so; das hat man die Werte für sich stehend und hängt denen Namensschilder um. Das ist halt schon etwas anderes, das man vielleicht unterscheiden sollte.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@nezzcarth: Der Wert ist variabel den man zuweisen kann, das ist keine Konstante. Und das sind am ehesten ”echte” Variablen, weil das so funktioniert wie Variablen in der Mathematik. Die kann man ja auch nicht verändern wenn sie einmal zugewiesen sind, das ist nicht das auf was sich das „variabel“ bezieht. Der Wert einer Variablen ist beliebig aber fest. Und diese Definition ist auch der eigentliche Klassiker, denn diese Variablen im Zusammenhang mit Funktionen gab es schon laaaange vor elektronischen Rechnern. Gibt ja auch nicht wenige die sich damals bei BASIC an so etwas wie ``A=A+1`` gestört haben, weil das mathematisch Unsinn ist, und Sprachen wie Pascal deshalb ``:=`` für Zuweisungen verwenden.

Das Variablen so funktionieren müssen wie in Pascal denken Leute die mit Pascal & Co aufgewachsen sind. Auch vor Pascal gab es schon Programmiersprachen die Variablen wie Python gehandhabt haben. Und auch aktuell nicht wenige bei denen das nicht wie Pascal funktioniert, und doch nennen die meisten diese Verbindung zwischen Namen und Werten „Variablen“. Hat JavaScript aus Deiner Sicht keine Variablen? Wofür steht dann das Schlüsselwort ``var``?

Ich habe Variablen zuerst mit BASIC kennengelernt und da gab's die gleiche Erklärung wie bei Pascal. Dann kamen einige Sprachen bei denen sich das auch so verhält, und ich dachte ich kann programmieren. Dann kam Haskell — man darf Variablen nur einmal einen Wert zuweisen und es gibt keine Schleifen — hä? Bei Java gilt das „Werte in Schachteln“ noch für die ”primitiven” Typen, Objekttypen verhalten sich wie in Python. Und bei Übersetzerbau habe ich dann gelernt das eine Variable in der Regel aus einem Namen, einem Wert, und einer Speicheradresse besteht, und das es von der Sprache abhängt ob die Adresse zum Namen oder zum Wert gehört. Und ob man Namen mehr als einmal an einen Wert binden darf. Das Konzept „Variable“ ist halt nicht selbsterklärend oder zumindest erklärt das nicht alles was man wissen muss. Da gehören je nach Sprache noch Informationen über die genaue Semantik und eventuelle Einschränkungen dazu.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
jupa
User
Beiträge: 4
Registriert: Freitag 7. Juni 2024, 13:36

__blackjack__ hat geschrieben: Freitag 7. Juni 2024, 18:16
Was ist denn das konkrete Problem was Du damit lösen wollen würdest?

Gar keines. Ich bin gerade noch in der Phase des Kennenlernens, Ausprobierens, Austestens nach dem Motto "Was passiert wenn ..." und bin dabei leider einem dummen Denkfehler aufgesessen. Inzwischen ist diesbezüglich alles geklärt und ich kann mich nur nochmals bei allen für die tolle Hilfsbereitschaft bedanken.

Grüße
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jupa: Dann kannst Du das ja mal im Hinterkopf behalten und später, wenn Du mehr kennengelernt und vielleicht auch mal ein aus Anfängersicht etwas grösseres Programm selbst geschrieben hast, darüber nachdenken wie einfach das tatsächliche Verhalten bei der Zuweisung implementiert werden kann, und wie viel mehr Aufwand das Verhalten was Du erwartet hattest, machen würde. Und auch was Probleme sich mit Funktionen ergeben würden die selbst ja auch wieder komplexe Rechnungen mit den übergebenen Parametern anstellen können, die dann ja auch in dieses Verhalten mit eingehen — oder auch nicht. Denn wie gesagt, möchte man das Verhalten ja nicht immer, es müsste also einen Weg geben das auf Wunsch auch zu unterdrücken.

Noch mal kurz zu der Nebendiskussion über den Begriff „Variable“: Das berührt die gestellte Frage nicht wirklich. Sprachen wo Variablen sich wie in Pascal verhalten, funktionieren bezüglich der Fragestellung genau wie Python. Hier das Beispiel in klassischem BASIC (mit Testlauf) …

Code: Alles auswählen

LIST

10 A=10
20 B=15
30 C=A+B
40 PRINT C
50 A=20
60 PRINT A
70 PRINT C
READY.
RUN
 25
 20
 25

READY.
… und Pascal:

Code: Alles auswählen

program Variables;
var a, b, c: Byte;
begin
  a := 10;
  b := 15;
  c := a + b;
  WriteLn(c); { Output: 25 }
  a := 20;
  WriteLn(a); { Output: 20 }
  WriteLn(c); { Output: 25 }
end.
Man kann sogar in Haskell Code schreiben der wie imperativer Code aussieht:

Code: Alles auswählen

main = do
    let a = 10
        b = 15
        c = a + b
    print c

    let a = 20
    print a

    print c
Das sind am Ende aber zwei verschiedene `a`-Variablen.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Benutzeravatar
sparrow
User
Beiträge: 4501
Registriert: Freitag 17. April 2009, 10:28

Vielleicht noch als ergänzender Hinweis ohne direkten Bezug auf die Frage: Auch in relativ neuen Sprachen gibt es durchaus unterschiedliche Herangehensweisen was 'Variablen' angeht. Wenn ich mich richtig erinnere sind in Rust alle Variablen unveränderlich, es sei denn sie bekommen das Flaf 'mut'. Und JavaScript schafft es mich durchgehend mit 'let' und 'var' zu verwirren.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@jupa: Du kannst das ja vielleicht mal im Hinterkopf behalten und wenn Du weiter im Thema bist, mal eine einfache Programmiersprache selber implementieren und dort das von Dir ursprünglich erwartete Verhalten einbauen. Ein einfacher BASIC-Dialekt reicht da ja schon.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
LeSchakal
User
Beiträge: 25
Registriert: Dienstag 5. Februar 2019, 23:40

Die einfachste Lösung für die Shell wird hier aus (nachvollziehbaren?) Gründen nicht erwähnt.

Code: Alles auswählen

>>> g = 'a + b'
>>> g
'a + b'
>>> a = 10
>>> b = 20
>>> eval(g)
30
>>> a = 20
>>> eval(g)
40
Wenn ich die Shell als Art Taschenrechner nutze, ist das mitunter praktisch.
In Programmen hat das eher nichts verloren.
Benutzeravatar
sparrow
User
Beiträge: 4501
Registriert: Freitag 17. April 2009, 10:28

'eval' hat in einem Programm nichts zu suchen. Und ich sehe auch nicht, warum das in deinem Beispiel in der Shell sinnvoll sein könnte.
Geneigte Leser sollten also vergessen, dass es Funktion gibt und einfach a+b eingeben.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1205
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Dieses Video schauen: https://www.youtube.com/watch?v=_AEJHKGk9ns

Ich wette, dass sogar die alten Hasen überrascht sein werden. Mir hat es damals weitergeholfen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten