@BlackJack: Ich denke, dass sich Leonidas der Sache bewusst ist und es nur etwas unglücklich formuliert hat.
@Darii: Jop, bei der Parameterübergabe wird nix unterschieden.
Hier mal ein kurzes Beispiel zur Veranschaulichung, was ich meinte:
Code: Alles auswählen
class MutableInt(int):
def __init__(self, *args, **kwargs):
self.v = int(self)
def __iadd__(self, other):
self.v += other
return self
def __repr__(self):
return repr(self.v)
def __str__(self):
return self.__repr__()
def inc(num):
print num, id(num), ' op +1 ',
num += 1
print num, id(num),
return num
a = MutableInt(1)
b = int(1)
print 'vor :', a, id(a), b, id(b)
a += 1
b += 1
print 'op +1'
print 'nach:', a, id(a), b, id(b)
print
a = MutableInt(1)
b = int(1)
print 'a:', 'c(', a, id(a), 'f(',
inc(a)
print ')', a, id(a), ')'
print 'b:', 'c(', b, id(b), 'f(',
inc(b)
print ')', b, id(b), ')'
Ausgabe ist:
Code: Alles auswählen
vor : 1 3082954988 1 134543200
op +1
nach: 2 3082954988 2 134543188
a: c( 1 3082955084 f( 1 3082955084 op +1 2 3082955084 ) 2 3082955084 )
b: c( 1 134543200 f( 1 134543200 op +1 2 134543188 ) 1 134543200 )
vor: / nach:
zeigt nur den Unterschied zwischen mutable/immutable und wie erwartet ändert der Typ 'int' die Objekt-ID während sie für 'MutableInt' gleich bleibt.
Interessant ist, was die Unterschiede im Zusammenspiel mit einem zusätzlichen Funktionsnamensraum ausmachen:
a:
Für einen mutable Typen sieht das Verhalten phänomenologisch nach 'call by reference' aus. Auch die Betrachtung der Objekt-ID lässt diesen Schluss zu. So werkeln alle am selben Objekt und die Manipulationen schlagen auch auf die
caller-Ebene durch. (Wie gesagt lasse ich die Implementationsdetails aussen vor und betrachte nur die Sprachebene, so interessiert hier auch erstmal nicht die Frage, ob die IDs trivial erreicht werden und noch einer Referenzdefinition standhalten.)
b:
Hmm, hier gibts Unterschiede in der Behandlung von Wert und Objekt-ID. Betrachtet man nur die Werte, liegt der Schluss nahe - 'call by value'. Die Objekt-IDs sehen aber eher nach 'call by reference' aus. Ich denke, dass hier der Hund in Sachen Verständnisschwierigkeiten begraben liegt. Tatsächlich macht Python hier nix anderes als unter 'a:' und reicht Objektreferenzen weiter (ID bleibt zunächst gleich). Immutable Typen sind aber scheu wie ein Reh und mit jedem Manipulationsversuch schiebt uns Python ein neues Objekt unter. Daher ändert sich die ID mit der Operation (+1) und der neue Wert (2) wird von einem neuen Objekt repäsentiert. Der Bezeichner 'num' zeigt jetzt auf ein anderes Objekt. (Das Gleiche passiert oben unter 'vor:'/'nach:' für immutable Typen.) Beim Verlassen des Funktionsnamensraumes fallen wir wieder auf das alte Objekt zurück (Wert und ID gleich denen zu Anfang), der
caller sieht also nichts von den Veränderungen. Tatsächlich gabs auch gar keine Veränderung am Ausgangsobjekt (immutable halt

) sondern nur eine neue "Objektbindung" durch die Operation (+1) an 'num' innerhalb der Funktion (wodurch klar sein sollte, dass das ausserhalb nicht gesehen wird).
Um diese vermeintlichen Ungereimtheiten zu beschreiben, finde ich die Nutzung einer Bezeichnung jenseits von 'call by value' und 'call by reference' gerechtfertigt. Vermeintlich deshalb, weil die Unterschiede nichts mit der eigentlichen Parameterübergabe zu tun haben sondern auf eine Objekteigenschaft zurückzuführen sind (greift erst zum Zeitpunkt der Manipulation, vorher lag tatsächlich das Ausgangsobjekt vor) und ich glaube, dass es diese Auseinandersetzung ohne immutable Typen garnicht erst gäbe und alle Welt einem 'call by reference'-Verständnis auf Sprachebene folgen würde (auch wenn die tatsächliche Implementation die Referenzen als Wertparameter übergibt.)
So genug pamphletisiert

Grüße, jerch