Parameter in Python
Moin, ich hatte ein interessante und zugleich verwirrende Diskussion über Funktionsparameter im IRC Chat #python. Das Problem dreht sich um mutable / immutable Werte. Beispiel Zahl als Funktionsparameter -> Veränderung nur lokal sichtbar. Beispiel Liste als Funktionsparameter -> Veränderung global sichtbar. Auf meine Frage, ob letzteres nicht call-by-reference (cbr) sei, erwiderte man das sei alles, wirklich alles call-by-value (cbv) weil man da ja Zeiger auf Objekte als Werte übergebe. Für mich ist das eine befremdliche Auffassung (und vielleicht auch Taktik um von einem Manko abzulenken), denn hinter cbr und cbv steht in erster Linie eine Semantik für den Programmierer und nicht die Frage wie ich die Parameter implementiere. So jedenfalls habe ich es damals gelernt. Mich würde mal interessieren, warum dieses Problem nicht schon längst angegangen wurde und ob ihr schon damit Kontakt hattet. Ich jedenfalls muss meine mutables allehand kopieren, um unerwünschte Seiteneffekte zu vermeiden. Grüße, Alexander
Alexander Langer schrieb:
Moin,
Guten Morgen,
ich hatte ein interessante und zugleich verwirrende Diskussion über Funktionsparameter im IRC Chat #python.
Ich glaube, dass es sich hier *nicht* um mutable/immutable dreht. Der Mechanismus, wie *Variablen* in ein Unterprogramm übergeben werden, hat nichts mit Immutabilität zu tun, IMO. Es mag zwar den Anschein erwecken, ist aber eine dazu orthogonale Eigenschaft.
Das Problem dreht sich um mutable / immutable Werte.
Beispiel Zahl als Funktionsparameter -> Veränderung nur lokal sichtbar. Beispiel Liste als Funktionsparameter -> Veränderung global sichtbar.
Eine Zahl ist immer unveränderlich, da sie atomar ist. Es ist noch niemanden gelungen, aus einer 12 eine 13 zu machen. Eine Zahl ist also *immer* immutable. Eine Liste hingegen kann veränderbar sein, oder eben nicht. Das hat jetzt aber nichts damit zu tun, wie sie als Parameter übergeben wird.
Auf meine Frage, ob letzteres nicht call-by-reference (cbr) sei, erwiderte man das sei alles, wirklich alles call-by-value (cbv) weil man da ja Zeiger auf Objekte als Werte übergebe.
Das ist korrekt! Die Begriffe Call-by-Referece und Call-by-Value beziehen sich eher auf die Variablen, die man dem Unterprogramm übergibt.
Für mich ist das eine befremdliche Auffassung (und vielleicht auch Taktik um von einem Manko abzulenken), denn hinter cbr und cbv steht in erster Linie eine Semantik für den Programmierer und nicht die Frage wie ich die Parameter implementiere. So jedenfalls habe ich es damals gelernt.
Wenn du eine Variable Call-by-Reference runterreichst, erlaubst du der Funktion, den _Inhalt_ der Variablen im Aufrufenden zu ändern (zum Beispiel: zu überschreiben). Du könntest also auch eine völlig andere neue Liste in die Variable stopfen. In folgendem Pascal-Beispiel: procedure swap(var a, b : integer) var t : integer; begin t :=; a :=; b :=; end; v1 :=2; v2 :=; swap(v1, v2); werden tatsächlich die Inhalte von v1 und v2 geändert/ausgetauscht. Wenn die Funktion (hier eine Prozedur) also auf 'a' arbeitet, arbeitet sie in Wirklichkeit mit der Variablen 'v1'. Wie dieser Mechanismus realisiert ist, ist Sache des Compilers. Als Programmierer musst du nur wissen, dass deine Variablen geändert werden können. Davon zu unterscheiden ist, wie ein Objekt in einer Variablen gehalten wird. Ob per Referenz auf das Objekt, oder als Wert. Achtung: Eine Variable mag eine Referenz auf eine Liste halten. Das ist aber unabhängig davon, wie diese Variable nach "unten" übergeben wird. Also ob der Wert (die Referenz auf die Liste), oder eine Referenz auf die wert-haltende Variable runtergereicht wird.
Mich würde mal interessieren, warum dieses Problem nicht schon längst angegangen wurde und ob ihr schon damit Kontakt hattet. Ich jedenfalls muss meine mutables allehand kopieren, um unerwünschte Seiteneffekte zu vermeiden.
Hmmm ... ich denke, das "Problem" wurde nicht angegangen, da es keines ist! Es handelt sich hier um zwei völlig verschiedene Konzepte, wie ich oben dargelegt habe. Diese zwei Konzepte ergänzen sich, beeinflussen sich aber nicht! Oftmals spielen auch einfachere Implementierung des Compilers, oder Performance-Überlegungen eine Rolle bei der Frage, ob komplexere Objekte nun als Werte oder Referenzen in einer Variablen gehalten werden. Wenn du es mit großen Listen zu tun hast, und diese jedes Mal kopieren musst, wenn du sie einem Unterprogramm übergibst, wirst du schnell auf einen üblen Engpass stoßen nach mehr Performance rufen! In Python hat man sich nunmal dafür entschieden, Listen als Referenzen in Variablen zu halten. Grüße, cle.
Mich würde mal interessieren, warum dieses Problem nicht schon längst angegangen wurde und ob ihr schon damit Kontakt hattet. Ich jedenfalls muss meine mutables allehand kopieren, um unerwünschte Seiteneffekte zu vermeiden.
Zu dem Rest hat Clemens schon viel richtiges gesagt. Mich wuerde eher interessieren, wieso du so viel zu kopieren hast. Das kommt bei mir gelegentlich auch mal vor - ist aber eher die Ausnahme. Vielleicht ist dein Problem dann doch anders gelagert, bzw. du benutzt Python nicht idiomatisch? Diez
Hallo Alexander, On 2013-11-12 06:13, Alexander Langer wrote:
ich hatte ein interessante und zugleich verwirrende Diskussion über Funktionsparameter im IRC Chat #python.
Das Problem dreht sich um mutable / immutable Werte.
Beispiel Zahl als Funktionsparameter -> Veränderung nur lokal sichtbar. Beispiel Liste als Funktionsparameter -> Veränderung global sichtbar.
Wie Clemens schon ausgeführt hat, sind die Parameter-Übergabe und Veränderbarkeit der Objekte selbst orthogonale Konzepte.
Auf meine Frage, ob letzteres nicht call-by-reference (cbr) sei, erwiderte man das sei alles, wirklich alles call-by-value (cbv) weil man da ja Zeiger auf Objekte als Werte übergebe.
Ich habe für die Parameter-Übergabe in Python mal den Begriff "call by assignment" gelesen. Das führt zwar einen neuen Begriff ein, aber ich finde den Ausdruck gut, weil die Semantik bei der Übergabe die gleiche ist wie bei einer Zuweisung `obj = some_expression`. Es wird nur ein Objekt mit einem Namen verknüpft. Änderungen an "immutable" Objekten (Zahlen, Strings) sind deshalb nicht "global sichtbar", weil es keine Änderungen gibt, denn diese Werte sind ja, wie der Begriff schon sagt, unveränderlich. Bei "mutable" Objekten, wie einer Liste, _kannst_ du das Objekt selbst ("in-place") verändern. Vergleiche
def unchanged(list_): ... list_ = [4, 5, 6] ... L = [1, 2, 3] unchanged(L) L [1, 2, 3]
und
def changed(list_): ... list_[:] = [4, 5, 6] ... L = [1, 2, 3] changed(L) L [4, 5, 6]
Im ersten Fall handelt es sich um eine Zuweisung. Hier wird ein neuer lokaler Name `list_` angelegt und die Liste `[4, 5, 6]` zugewiesen. Die übergebene Liste "bekommt von der Zuweisung nichts mit" und wird nicht verändert. Im zweiten Fall wird durch die Slice-Zuweisung das übergebene Listen-Objekt selbst ("in-place") verändert, so dass diese Änderungen auch nach Verlassen der Funktion sichtbar sind. Anstelle der Slice-Zuweisung könnte jede andere Anweisung stehen, die die Liste verändert, zum Beispiel `list_.append(1)` oder `list_.pop()`. Der zweite Funktionsaufruf ist äquivalent zu diesem Verhalten ganz ohne Funktionsaufruf:
L = [1, 2, 3] list_ = L # <- entspricht "call by assignment" list_[:] = [4, 5, 6] list_ [4, 5, 6] L [4, 5, 6]
Noch ein Experiment:
def func(value): ... print value is a ... a = 1 func(a) True a = [1, 2, 3] func(a) True
Auch hier ist wieder die Zuweisungs-Semanktik sichtbar. Das Verhalten ist das gleiche für unveränderliche und veränderliche Objekte. Der Vergleich mit `is` zeigt, dass keine Kopie des im Aufruf übergebenen Objektes erzeugt wurde, sondern man direkt auf das übergebene Objekt zugreift. Das sieht also nicht nach "call by value" aus. "Call by reference" passt aber auch nicht, denn _dann_ müsste, wie von Clemens erklärt,
def func(L1, L2): ... L1, L2 = L2, L1 ... a = [1, 2, 3] b = [4, 5, 6] func(a, b) a [1, 2, 3] b [4, 5, 6]
die Inhalte der übergebenen Listen vertauschen, was aber nicht geschieht. Weil weder "call by value" noch "call by reference" richtig passen, finde ich die Verwendung von "call by assignment" ganz vernünftig, - auch wenn man das erklären muss. :-) Zum letzten Beispiel könnte man allerdings einwenden, dass die (Tupel-)Zuweisung `L1, L2 = L2, L1` ja nichts mit der Übergabe zu tun hat (siehe oben). ;-)
Mich würde mal interessieren, warum dieses Problem nicht schon längst angegangen wurde und ob ihr schon damit Kontakt hattet. Ich jedenfalls muss meine mutables allehand kopieren, um unerwünschte Seiteneffekte zu vermeiden.
Wie gesagt, es gibt _keine_ unterschiedliche Parameter-Übergabe für veränderliche und unveränderliche Objekte. Entscheidend ist, was du mit den Objekten innerhalb deiner Funktion/Methode machst. Viele Grüße Stefan
Am 12.11.2013 06:13, schrieb Alexander Langer:
Auf meine Frage, ob letzteres nicht call-by-reference (cbr) sei, erwiderte man das sei alles, wirklich alles call-by-value (cbv) weil man da ja Zeiger auf Objekte als Werte übergebe.
Es ist weder call-by-reference noch call-by-value. Python verwendet call-by-object. Es werden Objekte übergeben. Wenn das Objekt nicht verändert werden kann, dann kann es eben nicht verändert werden. So einfach ist das. :) Spaß bei Seite, neue Pythonentwickler sind oft verwirrt, weil sie nur CBR und CBV in der Schule/Uni gelernt haben. Bei mutablen Objekten in Funktionsparametern kommt hinzu, dass es oft zu Missverständnissen kommt, wann diese Parameter ausgewertet werden. Die Defaultparameter werden definiert, wenn das Funktionsobjekt erstellt wird -- in der Regel beim Import des Moduls.
def func(param={'a': None}): ... pass ... func.__defaults__ ({'a': None},) func.__code__.co_varnames ('param',)
Christian
Am 12.11.2013 06:13 schrieb Alexander Langer:
Beispiel Zahl als Funktionsparameter -> Veränderung nur lokal sichtbar.
Hier ist wohl Neuzuweisung gemeint, was anderes geht hier auch nicht.
Beispiel Liste als Funktionsparameter -> Veränderung global sichtbar.
Eine Neuzuweisung ist hier auch nur lokal sichtbar. Eine Veränderung des Objektes selber hingegen ist in der Tat global sichtbar. l = l + [8]: Neuzuweisung l += [8]: Veränderung l.append(8): Veränderung (wenn l eine Liste ist)
Für mich ist das eine befremdliche Auffassung (und vielleicht auch Taktik um von einem Manko abzulenken),
Was für Manko?
Mich würde mal interessieren, warum dieses Problem nicht schon längst angegangen wurde
Problem? Eigenschaft. Thomas
participants (6)
-
Alexander Langer
-
Christian Heimes
-
Clemens Hintze
-
Diez B. Roggisch
-
Stefan Schwarzer
-
Thomas Rachel