Eine kleine Frage zu OOP (Python 3.1)

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.
Antworten
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

Hi. Folgende Frage:
Warum funktioniert das eigentlich?

Code: Alles auswählen

class A():
    def __init__(self):
        self.value = "a"
        
    def change(self, other):
        other.value = "b"

Code: Alles auswählen

>>> a = A()
>>> b = A()
>>> a.change(b)
>>> b.value
'b'
Eigentlich dürfte other in der Methode change doch nur lokal existieren oder? Normalerweise übergibt man doch keine Referenz auf ein Objekt oder so, sondern eine Kopie.
Ich find's gut und richtig, dass das klappt, sonst wäre es ziemlich umständlich, zwei Objekte miteinander kommunizieren zu lassen, aber ich frage mich eben, warum das klappt.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Das hat nichts mit OOP zu tun, sondern mit dem Aufrufverhalten bei Funktionen.
Es wird in Python kein Objekt kopiert, sondern das Objekt, bzw eine Referenz dazu uebergeben, Python betreibt zwar "Call by Value", allerdings ist der Wert eine Referenz und nur die wird kopiert.
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Normalerweise übergibt man doch keine Referenz auf ein Objekt oder so, sondern eine Kopie.
Nein!, Es sind NIE Kopien, wenn du nicht explizit nach einer fragst ('immutable' Objekte - z.B. int, str, tuple - machen die Sache für Anfänger aber nicht unbedingt durchschaubarer).
Du kannst ja in A.change() mal ein "print( other is b)" einbauen.
Die Übergaberegeln von anderen Sprachen, gelten ja nicht zwangsläufig in Python.
hth, Jörg
Wir haben schon 10% vom 21. Jahrhundert hinter uns!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Nocta hat geschrieben:Normalerweise übergibt man doch keine Referenz auf ein Objekt oder so, sondern eine Kopie.
Quatsch. Andersrum. Auch schon in Python 2.x
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

Ich bin nur dadurch etwas verwirrt, weil ja eigentlich gilt, dass in Funktionen ein eigener abgeschlossener Namensraum existiert und die einzige "Schnittstelle" nach Außen die return Anweisung ist, bzw eine globale Variable.
Eine normale Funktion ändert ja auch nicht die Werte auf Modulebene, nur weil man ihn als Argument bekommen hat. Er wird doch kopiert? Lieg ich da jetzt total falsch? Hilfe :D
Du kannst ja in A.change() mal ein "print( other is b)" einbauen.
Mir ist schon klar, dass er b kennt, bzw auf b zugreifen kann. Und other logischerweise gleich b ist. Nur weiß ich nicht, warum.

Sorry, falls das irgendwie total bescheuert rüberkommt, aber ich verstehe immer noch nicht, warum das Exemplar a über das Exemplar b bescheid weiß.
Ich hab das bisher immer als gegeben betrachtet und mich nie gefragt, wieso.
Das hat nichts mit OOP zu tun, sondern mit dem Aufrufverhalten bei Funktionen.
Bei normalen Funktionen klappt das doch nicht, oder? Zeig mir am besten mal ein Beispiel außerhalb von OOP, ich versteh glaube nicht ganz, was du meinst.


Tut mir leid, dass ich heute so begriffsstutzig bin :D
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nocta hat geschrieben:Ich bin nur dadurch etwas verwirrt, weil ja eigentlich gilt, dass in Funktionen ein eigener abgeschlossener Namensraum existiert und die einzige "Schnittstelle" nach Außen die return Anweisung ist, bzw eine globale Variable.
Eine normale Funktion ändert ja auch nicht die Werte auf Modulebene, nur weil man ihn als Argument bekommen hat. Er wird doch kopiert? Lieg ich da jetzt total falsch? Hilfe :D
Du liegst komplett falsch. Die Annahmen gelten nur fuer unveraenderliche Werte, die man neu zuweisen muss, um sie zu aendern. Vereinfacht gesagt: Objekte kann man immer veraendern (und sie bleiben es), jedoch nicht den Namen wechseln (in einem ausseren Scope).
Nocta hat geschrieben:Mir ist schon klar, dass er b kennt, bzw auf b zugreifen kann. Und other logischerweise gleich b ist. Nur weiß ich nicht, warum.
Weil die Referenz zu `b` in den lokalen Scope der Funktion kopiert wird.
Nocta hat geschrieben:Sorry, falls das irgendwie total bescheuert rüberkommt, aber ich verstehe immer noch nicht, warum das Exemplar a über das Exemplar b bescheid weiß.
Ich hab das bisher immer als gegeben betrachtet und mich nie gefragt, wieso.
Bei normalen Funktionen klappt das doch nicht, oder? Zeig mir am besten mal ein Beispiel außerhalb von OOP, ich versteh glaube nicht ganz, was du meinst.
Doch das tut es. Eine Methode ist nichts anderes als eine Funktion. `a` kennt `b`, weil du `a` mit einer Referenz auf `b` versorgst.

Code: Alles auswählen

In [1]: a = [1,2]

In [2]: def do_foo(x):
   ...:     x.append(42)
   ...:     return x
   ...:

In [3]: b = do_foo(a)

In [4]: a
Out[4]: [1, 2, 42]

In [5]: b
Out[5]: [1, 2, 42]

In [6]: b is a
Out[6]: True

Code: Alles auswählen

In [17]: def do_foo():
   ....:     x = 1
   ....:     print x
   ....:
   ....:

In [18]: x = "abc"

In [19]: do_foo()
1

In [20]: print x
-------> print(x)
abc
Wirds klarer?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Nocta hat geschrieben:Ich bin nur dadurch etwas verwirrt, weil ja eigentlich gilt, dass in Funktionen ein eigener abgeschlossener Namensraum existiert und die einzige "Schnittstelle" nach Außen die return Anweisung ist, bzw eine globale Variable.
Eine normale Funktion ändert ja auch nicht die Werte auf Modulebene, nur weil man ihn als Argument bekommen hat. Er wird doch kopiert? Lieg ich da jetzt total falsch? Hilfe :D
Du meinst so was?

Code: Alles auswählen

In [1]: a = 4

In [2]: def foo(bar):
   ...:     bar += 1
   ...:
   ...:

In [3]: foo(a)

In [4]: a
Out[4]: 4
Und dann das:

Code: Alles auswählen

In [5]: a = [42]

In [6]: def foo(bar):
   ...:     bar.append(69)
   ...:
   ...:

In [7]: foo(a)

In [8]: a
Out[8]: [42, 69]
?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Hyperion hat geschrieben:Du meinst so was?
Ja, ich denke, "so was" meint er. Noch als Ergänzung:

Code: Alles auswählen

>>> a = [42]
>>> def foo():
...     a[0] = 43
... 
>>> a
[42]
>>> foo()
>>> a
[43]
>>> a = [42]
>>> def foo():
...     a = [43]
... 
>>> a
[42]
>>> foo()
>>> a
[42]
@Nocta: Ich habe auch lange Zeit mit einem fehlerhaften Verständnis dieser Zusammenhänge gelebt und mir meine eigenen Erklärungen zurechtgelegt, so dass alles irgendwie zusammengepasst hat, bis mir irgendwann einige Unermüdliche hier im Forum mühsam die Augen geöffnet haben (den Thread gibt es noch irgendwo, vielleicht lohnt es sich, den zu suchen).
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

Hm, bisher hab ich das mit den immutable und mutable Datentypen immer mehr oder weniger ignoriert ...
Aber das ist, wenn ich das richtig verstehe, des Rätsels Lösung?

Bei immutable Datentypen wird also wirklich eine Kopie erstellt aber ansonsten wird eine Referenz übergeben?
Noch eine Frage dazu: Selbsterstellte Datentypen sind standardmäßig immer mutable, da ich bisher noch nie Probleme hatte, dass x(self, other) funktioniert und auch other verändert, also eine Referenz übergeben wird. Wie schafft man es dann, dass ein selbstdefinierter Datentyp immutable wird?

Und wie bewertet ihr persönlich das ganze Verhalten? Logisch und konsequent und vielleicht auch praktisch, oder fändet ihr es besser, wenn man Referenzen immer explizit angeben müsste, anstatt, dass hier solche Verwirrungen entstehen? So wie das in anderen Sprachen gehandhabt wird, so weit ich da informiert bin.

Oder sind die Fragen jetzt total dumm? Vielleicht sollte ich erstmal das hier ganz verdauen, bevor ich weiter frage.
Aber an Fragen merkt man auch bekanntlich am besten, ob derjenige (in dem Fall ich) etwas verstanden hat oder nicht.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nocta hat geschrieben:Hm, bisher hab ich das mit den immutable und mutable Datentypen immer mehr oder weniger ignoriert ...
Aber das ist, wenn ich das richtig verstehe, des Rätsels Lösung?
Ja. Das Verhalten ist bei Immutables dasselbe, allerdings kann man die nicht veraendern, also tritt das Problem nicht auf.
Nocta hat geschrieben:Bei immutable Datentypen wird also wirklich eine Kopie erstellt aber ansonsten wird eine Referenz übergeben?
Nein, auch hier wird die Referenz kopiert, das Verhalten ist exakt gleich.
Nocta hat geschrieben:Und wie bewertet ihr persönlich das ganze Verhalten? Logisch und konsequent und vielleicht auch praktisch, oder fändet ihr es besser, wenn man Referenzen immer explizit angeben müsste, anstatt, dass hier solche Verwirrungen entstehen? So wie das in anderen Sprachen gehandhabt wird, so weit ich da informiert bin.
Nur weil es andere Sprachen so machen, muss Python da nicht nachziehen, allerdings folgen viele Sprachen auch dem Weg, den Python geht. Man koennte natuerlich Syntax fuer das Uebergeben einer Kopie des Objekts einfuehren, allerdings steht man dann evtl vor demselben Problem, das bspw. C++ hat und ein explizites Kopieren folgt dem Zen von Python und macht die Absicht deutlicher.
Das Verhalten ist konsequent und logisch, sofern man sich klar wird, dass das Verhalten nicht unterschiedlich ist, sondern Immutables einfach immutable sind und deshalb das "Problem" nicht existiert.
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

Nocta hat geschrieben:(...) oder fändet ihr es besser, wenn man Referenzen immer explizit angeben müsste, anstatt, dass hier solche Verwirrungen entstehen? So wie das in anderen Sprachen gehandhabt wird, so weit ich da informiert bin.
Perl handhabt das genau so: Es wird streng unterschieden zwischen Referenz und Wert, mit der Folge, dass man explizit derefernzieren muss, dort wo es nötig ist. Viele Neulinge streichen beim Thema Referenzen in Perl schnell die Segel - zumal die Dereferenzierung in Perl auch noch kontextabhängig ist, also z.B. zwischen Skalarem und Listen-Kontext unterschieden wird. Python arbeitet die meiste Zeit auch mit Referenzen, versteckt das aber sehr gut durch die automatische Dereferenzierung. In den meisten Fällen funktioniert das so auch völlig schmerzfrei, außer z.B. wenn man die Kopie einer Liste an eine Funktion übergeben möchte - dann muss man das per [:] machen, sonst hat die Funktion u.U. ungewünschte Seiteneffekte.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Nocta hat geschrieben:Wie schafft man es dann, dass ein selbstdefinierter Datentyp immutable wird?
Beispielsweise indem du alle Schreibzugriffe abfängst ( mit __setattr__).
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Aber das ist, wenn ich das richtig verstehe, des Rätsels Lösung?

Bei immutable Datentypen wird also wirklich eine Kopie erstellt aber ansonsten wird eine Referenz übergeben?
Nein. Namen (in einem bestimmten Namensraum, also zb dem globalen, oder dem lokalen einer gerade ausgeführten Funktion) sind einfach Bindungen an ein beliebiges Objekt. Und da in Python alles ein Objekt ist, kann man im Grunde alles an einen Namen binden. Und das ist alles. Objekte kennen ihre Namen nicht! Die Namen sind den Objekten somit herzlich egal - ob sie an einen Namen gebunden sind oder zb in einer Liste existieren, spielt absolut keine Rolle für sie.

Alles andere ist eine Angelegenheit des Objektes selbst. Die Namen haben damit nichts mehr zu tun. Hier zb ein Objekt, welches sich "veränderlich" verhält:

Code: Alles auswählen

class MutableObject(object):
    def __init__(self):
        self.data = []
    def __add__(self, other):
        self.data.append(other)
        return self
    __iadd__ = __add__
(Nicht getestet, habe aktuell keinen Interpreter zur Hand.)
Wenn du jetzt dieses Objekt (auf der linken Seite) mit einem anderen Objekt addierst, kommt dabei dieses Objekt wieder zurück - allerdings wurde data verändert. Führst du also

Code: Alles auswählen

a = MutableObject()
b = a + 5
Dann ist a das selbe Objekt wie b - a is b ergibt True.

Führ den Rest verlinke ich mal auf Numerix's Thread, da steht das nochmal - und irgendwo gabs da noch nen schönen Blogpost zu. Im Grunde ist das System sehr einfach, wenn man mal die Grundidee verstanden hat.
Wie schafft man es dann, dass ein selbstdefinierter Datentyp immutable wird?
Du solltest dich zuerst Fragen, warum ein Objekt immutable sein kann, beziehungsweise warum das Konzept eines unveränderlichen Objektes relativ sinnvoll ist.

Ein unverändliches Objekt ist ein "Wertobjekt" - es macht nur deswegen Sinn, es nicht verändern zu können, weil es selbst einen Wert darstellt. Das ist der Unterschied zu veränderlichen Objekten: Die existieren nicht um ihrer selbst Willen, sondern aus bestimmten Gründen. Ich habe in einem typischen Programm niemals eine Liste weil ich ihren Wert bräuchte - der Wert einer Liste gibt Angaben über ihren Inhalt (und in einer Weise, die für Listen sinnvoll ist - ein Dictionary implementiert natürlich eigene Methoden, seinen "Wert" zu bestimmen). Ein unverändliches Objekt, ein Wertobjekt, dagegen, brauche ich einfach um seiner selbst Willen. Stell dir vor, du hast ein Programm in dem es keine Zahl / keinen Integer Eins gibt. Du wärst aufgeschmissen. Das Objekt für die Zahl 1 stellt selbst einen Wert dar.

Wenn du eine Art unverändliches Objekt programmieren willst, musst du einfach nur dafür sorgen, daß es keine von außen zugänglichen Attribute hat, die es verändern kann. Also alle mit einem Unterstrich als "privat" markieren. Alle Methoden dürfen das Objekt (mit Ausnahme von internem Zustand, der ja grade als privat markiert wurde) nicht verändern. Brauch man unbedingt eine veränderte Version des Objektes, muss man ein neues erzeugen und zurückgeben. Und da kommt das "Wertobjekt" wieder zum Vorschein: Für alle anderen Dinge sind solche unveränderlichen / komplett unveränderlichen Objekte nicht sehr sinnvoll.
Und wie bewertet ihr persönlich das ganze Verhalten? Logisch und konsequent und vielleicht auch praktisch, oder fändet ihr es besser, wenn man Referenzen immer explizit angeben müsste, anstatt, dass hier solche Verwirrungen entstehen? So wie das in anderen Sprachen gehandhabt wird, so weit ich da informiert bin.
Ich finds gut. Klar, ist am Anfang vllt etwas verwirrend, aber das wars auch. Es läuft letztendlich nur darauf hinaus, daß das Objekt seine Verhaltensweisen selbst bestimmt und Namen nur eine Art "Zettel" an den Objekten sind, um sie im entsprechenden Namensraum zu halten und anzusprechen. Übergebe ich ein Objekt einem anderen, bestimmt dieses, wie es das erstere aufbewahrt - all das hat nichts mehr mit dem Namensraum, in welchen die Übergabe / Erzeugung stattfand, zutun. Das Verhalten ist so deutlich besser, als alle andere, was ich in anderen Sprache gesehen habe.

Hoffentlich hab ich das nicht alles zu wirr geschrieben, wenn doch, frag einfach.

Der Thread von Numerix: http://www.python-forum.de/topic-16761.html
Der erwähnte Blogpost: http://the-space-station.com/2008/11/22 ... -variables
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

Okay vielen Dank, für eure ganzen Antworten.
Sehr freundlich von euch, dass ihr mir das ganze so detailliert erklärt und mir auch Links hinterherwerft :)
Das meiste was ihr mir erzählt habt, ist zwar nicht wirklich neu für mich, aber es hat mir geholfen das ganze noch mal zu überdenken und in meinem Kopf umzustrukturieren.

Ich denke ich sehe das ganze jetzt klarer ...
Ich habe vorhin ja gesagt, dass bei immutable Datentypen eine Kopie erstellt wird. Jetzt ist mir auch klar, dass es nicht so ist, sondern in Wirklichkeit bei einer Zuweisung (o.Ä) einfach ein neues Objekt entsteht, welches dann nichts mehr mit dem alten zu tun hat.
Aber man könnte sagen, dass es sich bei immutable Datentypen so verhält, als ob eine Kopie erstellt würde, oder ist das auch wieder falsch?

Noch eine abschließende Frage:
Könnte man sagen, dass Objekte nicht zwingend in allen Eigenschaften mutable/immutable sind, sondern gemischt?
Ich habe immer gedacht, dass ein Objekt immer entweder mutable oder immutable ist.
Aber wenn ich jetzt mal str1442's Beispiel mit dem MutableObject um eine __sub__ Methode erweitern würde, die ein neues MutableObject (also nicht self) zurückgibt, würde doch b = a + 5 wie im Beispiel das Objekt verändern, es verhält sich also mutable, und bei b = a - 5 würde ein neues Objekt liefern, sich also immutable verhalten. Richtig?
BlackJack

@Nocta: Nicht die Zuweisung kopiert ein "immutable"s Object, sondern eventuell eine Operation, die man darauf aufruft. Auf der Ebene der Zuweisung passiert immer das Gleiche, egal was für ein Objekt da zugewiesen wird.

Klar kannst Du Dir vorstellen, dass immutable Objekte kopiert werden. Aber *warum*? Damit hast Du in Gedanken eine Unterscheidung, die keine Auswirkungen hat. Und das *nicht* kopiert wird, kann man mit `id()` oder ``is`` ganz einfach überprüfen. Du stellst Dir da also etwas vor was nicht stimmt. Ist es nicht viel einfacher keine Unterscheidung zu machen und die Dinge so zu sehen wie sie sind, nämlich das *alle* Objekte in dieser Hinsicht *gleich* behandelt werden!?

Gemischte Objekte gibt es IMHO nicht. Wenn man auch nur irgend etwas am inneren Zustand eines Objektes über die öffentliche API verändern kann, dann ist es nicht mehr immutable. Dann kann man es nicht einfach durch ein anderes Objekt mit dem gleichen Wert austauschen, weil sie sich eben doch unterscheiden. Das es bei mutable Objekten auch Methoden gibt, die nur etwas abfragen und den Zustand nicht verändern, ist so verbreitet, dass dann so ziemlich jedes Objekt als "Mischobjekt" angesehen werden muss. Das macht nicht viel Sinn.
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

BlackJack hat geschrieben:@Nocta: Nicht die Zuweisung kopiert ein "immutable"s Object, sondern eventuell eine Operation, die man darauf aufruft. Auf der Ebene der Zuweisung passiert immer das Gleiche, egal was für ein Objekt da zugewiesen wird.
Ja ich hab mich etwas geschickt ausgedrückt, aber du hast natürlich recht damit :)
Klar kannst Du Dir vorstellen, dass immutable Objekte kopiert werden. Aber *warum*? Damit hast Du in Gedanken eine Unterscheidung, die keine Auswirkungen hat.
Naja, in der Physik stellt man sich auch vereinfacht anhand von Modellen Dinge vor, die gar nicht so sind :) Aber vielleicht hast du auch recht und mein "Modell" vereinfacht das ganze nicht, sondern verkompliziert es.
Naja, meine Frage hast du ja beantwortet, man kann sich das also so vorstellen. Und das war eigentlich das Ziel meiner Frage. Ich will nicht unbedingt diese Vorstellung von einer Kopie in meinem Kopf verankern, sondern wollte eher das neu gelernte mit meinen alten Erfahrungswerten in Einklang bringen. Das ist glaube ich menschlich ;)
Gemischte Objekte gibt es IMHO nicht.
Okay, wenn man das so sieht, ist es wirklich sinnfrei, noch gemischte Objekte zu unterscheiden. Ich wollte nur noch mal sichergehen, ob ich das ganze richtig verstanden habe.
Datentypen sind also nicht zwingend in allen Eigenschaften immutable oder mutable, sondern es kommt darauf an, wie die jeweilige Operation definiert ist.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Nocta hat geschrieben:Naja, in der Physik stellt man sich auch vereinfacht anhand von Modellen Dinge vor, die gar nicht so sind :) Aber vielleicht hast du auch recht und mein "Modell" vereinfacht das ganze nicht, sondern verkompliziert es.
Weil in der Pyhsik die Modelle wesentlich komplizierter sind. Das Objektmodell in Python ist eigentlich recht simpel, man könnte direkt sagen primitiv. Ab dem Zeitpunkt ab dem man es verstanden aht, dass es eigentlich keine Unterscheidung zwischen Immutablen Objekten und mutablen Objekten gibt wird es direkt schon langweilig ;)

Naja, meine Frage hast du ja beantwortet, man kann sich das also so vorstellen. Und das war eigentlich das Ziel meiner Frage. Ich will nicht unbedingt diese Vorstellung von einer Kopie in meinem Kopf verankern, sondern wollte eher das neu gelernte mit meinen alten Erfahrungswerten in Einklang bringen. Das ist glaube ich menschlich ;)
Nocta hat geschrieben:Datentypen sind also nicht zwingend in allen Eigenschaften immutable oder mutable, sondern es kommt darauf an, wie die jeweilige Operation definiert ist.
IMHO kommt es eher darauf an was man mit dem Objekt macht: wenn es immutable ist kann man es verändern ansonsten erstellt man ein *neues* Objekt, das bestimmte Daten aus dem alten Objekt übernimmt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Nocta hat geschrieben:Datentypen sind also nicht zwingend in allen Eigenschaften immutable oder mutable, sondern es kommt darauf an, wie die jeweilige Operation definiert ist.
Wenn ein Datentyp hinsichtlich einer noch so kleinen kleinigkeit Mutabel ist, ist er nicht mehr Immutabel.

Mal an einem Beispiel. Sei Date eine Klasse, die ein Kalendarisches Datum darstellt.

Code: Alles auswählen

alice = Human()
bob = Human()
alice.birthday = Date(year=1984, month=5, day=23)
bob.birthday = alice.birthday
Alice und Bob haben nun am gleichen Tag Geburtstag.
Technisch verweisen sowohl alice.birthday und bob.birthday auf das selbe Objekt im Speicher.

So, man will nun irgendwelche Datumsrechnereien programmieren.

Wenn wir das mutabel machen, nämlich so:

Code: Alles auswählen

class Datum(object):
   # ...
   def add_day(self, days):
        self.day += days      # jaja, nicht perfekt
hätte das lustige Folgen, auch wenn das Datumn nach deiner Logik immer noch "immutabel hinsichtlich bestimmter eigenschaften", z.B. des Jahres ist.

Code: Alles auswählen

alice.birthday.add_days(1)
assert bob.birthday.day == 24
da sich die Eigenschaften des Datumsobjektes geändert haben, das Objekt allerdings mit bob geteilt wurde, hat man den direkt aus versehen mitgeändert.

Weshalb wir das Objekt immutabel machen, indem wir es IN KEINER HINSICHT mutabel machen:

Code: Alles auswählen

class Date(object):
    # ...
    def add_days(self, days):
        return Date(year=self.year, month=self.month, day=self.day + days)

alice.birthday = alice.birtday.add_days(1)
assert alice.birthday.day == 24
assert bob.birthday.day == 23
Hier wird nicht das alte Objekt geändert, sondern ein neues erzeugt, welches alice' dann als Geburtstag gesetzt bekommt.

Also: Mutabel ist wie Schwanger: "ein bisschen" oder "gemischt" geht nicht.
Antworten