Seite 1 von 1

Variable = Variable? Mal Wert mal Referenz?

Verfasst: Mittwoch 24. Mai 2006, 15:01
von nbkr
Hallo,

ich versuche gerade zu durchblicken wann Python Variablen als Wert und wann als Referenz übergibt.

Wenn ich eine Funktion aufrufe, dann macht Python die Übergabe als Wert, egal von welchem Typ die Variable ist, richtig?

Erzeuge ich eine einfache Variable wie "a = 5" und mache danach b = a und danach a = 6, dann müsste b immer noch 5 sein. D.h. Python kopiert hier die Variable echt.

Mache ich jedoch eine Liste zb. "a = [1, 2, 3]" und dann ein b = a und danach dein a.append(5), dann ist b hinterher auch [1, 2, 3, 5]. D.h. bei Variablen die ein Objekt oder eine Liste enthalten macht der = Operator keine Kopie sondern nur eine Referenz, richtig?

Kann man letzters irgendwie umgehen? Ich müsste eine Liste echt kopieren und nicht nur eine neue Referenz erzeugen?

Gruß
nbkr

Verfasst: Mittwoch 24. Mai 2006, 16:03
von Leonidas
Das geht auf mancherlei Weise:

Code: Alles auswählen

b = a[:]
import copy
b = copy.copy(a)
id(a)
id(b)
Das id()-Builtin ist für sowas auch sehr praktisch.

Verfasst: Mittwoch 24. Mai 2006, 16:06
von knekke
Wenn man in die Suche "Listen kopieren" eingibt landet man hier:
http://www.python-forum.de/topic-398.ht ... n+kopieren

Verfasst: Mittwoch 24. Mai 2006, 16:07
von nbkr
Hi, danke für die Tipps. Die Forensuche hatte ich verwendet, aber ich habe immer nach Variable Referenz Copy oder ähnliches gesucht. Auf das einfachst bin ich irgendwie nicht gekommen. Danke!

Verfasst: Donnerstag 25. Mai 2006, 00:11
von Joghurt
Python arbeitet immer mit Referenzen.

Bei einem "a=4" ist a eine Referenz auf das Integerobjekt 4.

Verfasst: Donnerstag 25. Mai 2006, 06:47
von nbkr
Das stimmt aber nicht bei Übergabe einer Variable an eine Funktion. Sonst würde die Änderungen die die Funktion vornimmt ja gleich auf die Variable durchschlagen.

Verfasst: Donnerstag 25. Mai 2006, 07:41
von BlackJack
nbkr hat geschrieben:Das stimmt aber nicht bei Übergabe einer Variable an eine Funktion. Sonst würde die Änderungen die die Funktion vornimmt ja gleich auf die Variable durchschlagen.
Doch das stimmt auch bei der Übergabe an eine Funktion. Python übergibt immer Referenzen. Und wenn Du ein Objekt innerhalb einer Funktion änderst, dann ist das auch immer ausserhalb sichtbar.

Du scheinst Dich durch so etwas hier verwirren zu lassen:

Code: Alles auswählen

a = 42
def test(val):
    val = 23
test(a)
print a  # -> 42
Es wird eine Referenz auf das int-Objekt 42 übergeben, nicht auf `a`. Und wenn man `val` nun an ein anderes Objekt bindet, hat das keine Auswirkungen auf `a`. Der Name `a` ist weiterhin an das int-Objekt 42 gebunden.

Verfasst: Donnerstag 25. Mai 2006, 07:48
von gerold
nbkr hat geschrieben:Das stimmt aber nicht bei Übergabe einer Variable an eine Funktion. Sonst würde die Änderungen die die Funktion vornimmt ja gleich auf die Variable durchschlagen.
Hi nbkr!

Der Unterschied ist nicht ob es eine Referenz zu einem Objekt ist, oder nicht, sondern ob der Variabletype mutable (veränderbar) oder immutable (unveränderbar) ist.

Und, JA --> Python arbeitet immer mit Referenzen.

Bei Python sind gewisse Variabletypen veränderbar und gewisse nicht. Das ist genau definiert.

Nicht veränderbare Variabletypen können nur überschrieben, nicht verändert werden.

Nicht veränderbare Variabletypen sind:
- String
- Integer
- Long
- Tupel

Veränderbare Variabletypen sind:
- Liste
- Dictionary

Hier ein kleines Beispiel:
Wenn ich einer Variable einer Zahl zuweise, dann wird, falls diese Zahl noch nie verwendet wurde, dieser Zahl ein Speicherplatz zugewiesen und die Variable ist der Name zu diesem Speicherplatz.

Code: Alles auswählen

>>> a = 5
>>> id(a)
134540408
>>>
Wenn ich die Zahl 5 zusätzlich einer anderen Variable zuweise, also noch einen Namen für die Zahl 5 vergebe, dann wird kein neuer Speicherplatz für eine neue Zahl reserviert, sondern einfach nur der neue Name dem Speicherplatz für die Zahl 5 zugewiesen. (Man vergleiche die ID.)

Code: Alles auswählen

>>> b = 5
>>> id(b)
134540408
>>>
Da bei nicht veränderbaren Datentypen immer auf den gleichen Speicherplatz verwiesen wird, darf der Inhalt des Speicherplatzes nicht mehr verändert werden, da ansonsten die Variablen (=Namen) plötzlich auf einen anderen Wert verweisen würden. Das ist bei nicht veränderbaren Datentypen nicht erwünscht.

Bei veränderbaren Datentypen verhält es sich komplett anders. Wird eine Variable einer Liste zugewiesen, dann wird immer eine neue Liste erstellt, einem Speicherplatz zugewiesen und die Variable auf den neuen Speicherplatz verwiesen. Somit ist die Basis veränderbarer Variablen, einfach nur ein Speicherplatz, der sich nicht mit anderen überschneiden kann. Reicht später der Speicherplatz nicht mehr aus, dann wird einfach zusätzlicher Speicherplatz angefordert. Das passiert transparent im Hintergrund und ist für den Programmierer nicht wichtig.

Code: Alles auswählen

>>> liste_1 = []
>>> id(liste_1)
-1216406356
>>> liste_2 = []
>>> id(liste_2)
-1216406068
>>>

Wie man im Beispiel sieht, wird der zweiten Liste eine neue Speicheradresse zugewiesen.

Wird einer neuen Variable eine bereits existierende, unveränderliche Variable zugewiesen, dann wird der neuen Variable einfach nur der unveränderliche Wert (oder dessen Speicherbereich) zugewiesen. Beide Variablen, die alte und die neue, hängen nicht zusammen, sondern stellen beide eine Referenz zu einem unveränderlichen Wert dar. Es gibt keinen Unterschied, ob ich beiden Variablen beim Erstellen den Wert oder eine Variable zugewiesen habe. Beide sind im Ergebnis gleich --> je eine Variable zu einem unveränderlichen Wert. Wird im Nachhinein der alten Variable ein neuer Wert zugewiesen, dann ändert sich zwar der Wert und der Speicherbereich der alten Variable, die neue Variable ändert sich aber nicht, da sie ja per Definition auf eine unveränderliche Variable verweist.

Code: Alles auswählen

>>> a = 5
>>> b = a
>>> id(a)
134540408
>>> id(b)
134540408
>>> a = 10
>>> a
10
>>> b
5
>>> id(a)
134540348
>>> id(b)
134540408
>>>
Bei veränderlichen Datentypen muss man unterscheiden, ob eine Variable einen neuen Wert bekommt, also einem neuen Speicherbereich zugewiesen wird, oder ob der Inhalt des Speicherbereichs geändert wird. Das ändern des Inhalts des Speicherbereichs ist ja bei veränderbaren Datentypen erlaubt. Wird der Variable ein neuer Speicherbereich zugewiesen, dann wird somit die Variable neu definiert, hat also keinen Bezug mehr zum alten Speicherbereich.

Code: Alles auswählen

>>> liste_1 = [10, 20]
>>> id(liste_1)
-1216406004
>>> liste_2 = liste_1
>>> id(liste_2)
-1216406004
>>> liste_1 = [30, 40]
>>> id(liste_1)
-1216405940
>>> id(liste_2)
-1216406004
>>> liste_1
[30, 40]
>>> liste_2
[10, 20]
In diesem Beispiel wird zuerst zwar der Variable "liste_2" der gleiche Speicherplatz wie der Variable "liste_1" zugewiesen, aber danach wird nicht der Wert des Speicherbereichs, sondern der Variable "liste_1" eine neue Liste, also ein neuer Speicherbereich, zugewiesen. Somit haben "liste_1" und "liste_2" keinen Bezug mehr zueinander.

Im nächsten Beispiel, wird zuerst der Variable "liste_2" der gleiche Speicherbplatz wie der Variable "liste_1" zugewiesen. Danach wird aber nur der Inhalt des Speicherbereichs verändert. Beide Variablen zeigen noch auf den selben Speicherbereich. Deshalb zeigen beide Variablen auch auf den selben Wert.

Code: Alles auswählen

>>> liste_1 = [10, 20]
>>> liste_2 = liste_1
>>> id(liste_1)
-1216406100
>>> id(liste_2)
-1216406100
>>> liste_1[0] = 30
>>> liste_1
[30, 20]
>>> liste_2
[30, 20]
>>> id(liste_1)
-1216406100
>>> id(liste_2)
-1216406100
>>>
Da also unveränderbare Datentypen per Definition nicht verändert werden können, kann man Variablen, die auf einen unveränderbaren Datentyp verweisen, immer nur überschreiben -- niemals verändern.

Auch wenn mehrere Variablen auf einen unveränderbaren Datentyp zeigen, ein Ändern einer dieser Variablen, weist dieser einen neuen Speicherbereich zu. Die anderen Variablen bleiben davon unbetroffen.

Auch wenn mehrere Variablen auf einen veränderbaren Datentyp zeigen, ein einzelnes Zuweisen eines neuen Speicherbereichs, einer dieser Variablen, wirkt sich nicht auf die anderen Variablen aus. Wird allerdings der Inhalt des Speicherbereichs verändert, dann wirkt sich das auf alle Variablen aus, die auf diesen Speicherbereich zeigen.

mfg
Gerold
:-)

PS: Weitere Lektüre: http://www.python-forum.de/topic-5903.html

Verfasst: Freitag 26. Mai 2006, 12:02
von nbkr
Danke für die mehr als ausführlich Antwort ;-)

Dann wird auch klar warum das Anhängen von Daten an die eine Liste dazu führt das die bei der "Kopie" (auch wenns keine ist) auch auftauchen.

Das ganze (im)mutable Konzept führt letzten Endes auch dazu, das Python keine explizite Übergabe per Referenz oder per Wert hat. Das ist quasi Built-In.

Übergibt man eine immutable Variable (eine einfache Zahl) und verändert deren Wert innerhalb der Funktion wird ein neuer Speicherbereich für die Zahl generiert. D.h. der Wert der Variablen ändert sich nur innerhalb der Funktion. Dies würde dem klassischen "Call by value" entsprechen.

Übergibt man eine mutable Variable (eine Liste) und hängt innerhalb der Funkktion einen Wert an, so passiert dies auch außerhalb (weil der Speicherbereich bei mutable Typen nicht geändert wird). Das würde also dem klassischen "Call by reference" entsprechen.

Um jetzt eine mutable Variable innerhalb einer Funktion ändern zu können ohne das es ein Problem mit dem Variablennamen außerhalb gibt, muss man innerhalb der Funktion also folgendes machen:

Code: Alles auswählen

def whatever(mylist):
    mylist = mylist[:]
Somit hätte man innerhalb der Funktion eine mutable Variable mylist die mit der Variablen mylist außerhalb der Funktion nichts mehr zu tun hat.

Bei imutable Variablen muss man deren neuen Wert wieder zurückgeben wenn man diesen außerhalb der Funktion wieder benötigt. Da Python mehr als einen Rückgabewert für eine Funktion kennt kann damit praktisch das Call by Reference andere Sprachen nachempfinden.

Hach, schön wenn man was lernt ;-)

Danke an alle!

Verfasst: Freitag 26. Mai 2006, 22:17
von BlackJack
nbkr hat geschrieben:Übergibt man eine mutable Variable (eine Liste) und hängt innerhalb der Funkktion einen Wert an, so passiert dies auch außerhalb (weil der Speicherbereich bei mutable Typen nicht geändert wird). Das würde also dem klassischen "Call by reference" entsprechen.
An dieser Stelle fangen dann die "religiösen" Kriege an. Ist das nun "call by reference" oder "call by value" wobei die Werte grundsätzlich Referenzen sind? Ich bin eigentlich für letzteres, weil es IMHO keinen Sinn macht von "call by reference" zu sprechen, wenn man als Programmierer gar nicht an die Referenz selbst heran kommt, wie z.B. in C, sondern grundsätzlich immer nur an den Wert auf den verwiesen wird.

Da gab's schon ellenlange Diskussionen in der englischsprachigen Python-Newsgroup. Unter anderem auch mit dem Vorschlag das "call by value/reference" beides auf Python nicht so richtig passt und man vielleicht lieber von "call by object" sprechen sollte
Um jetzt eine mutable Variable innerhalb einer Funktion ändern zu können ohne das es ein Problem mit dem Variablennamen außerhalb gibt, muss man innerhalb der Funktion also folgendes machen:

Code: Alles auswählen

def whatever(mylist):
    mylist = mylist[:]
Somit hätte man innerhalb der Funktion eine mutable Variable mylist die mit der Variablen mylist außerhalb der Funktion nichts mehr zu tun hat.
Doch, auch die Kopie enthält noch die selben Objekte und wenn die mutable sind, dann "sieht" man Änderungen natürlich auch ausserhalb der Funktion.