OOP Problem

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.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Darii hat geschrieben:
wobei die Unterscheidung zwischen veränderlichen und unveränderlichen Datentypen
Welche Unterscheidung? Python unterscheidet da nicht.
Natürlich nicht, jedoch verwirrt das viele Leute die Call By Reference verwenden dass ``a.append(b)`` das ursprüngliche ``a`` verändert, ``a += b`` das ursprünglich übergebene ``a`` unverändert lässt. Obwohl ja scheinbar in der aktuellen Funktion ``a`` "geändert" ist.

Deswegen die Idee, das eben nicht Call by reference zu nennen...
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@Leonidas: Ob ``a += b`` das ursprünglich an `a` gebundene Objekt unverändert lässt, hängt ganz von der Implementierung von `a.__class__.__iadd__()` ab. Bei Listen wird `a` zum Beispiel verändert.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@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 :wink:) 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
Antworten