Wozu "self"?

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.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Samstag 22. November 2008, 11:52

Python ist 10 Jahre jünger als Smalltalk, von dem AFAIK self kommt. Dort ist es implizit, muss also nicht in der Parameterliste einer Methode aufgeführt werden. Allerdings gibt es in Smalltalk auch nur Methoden, keine Funktionen.

In Smalltalk gibt es auch keine Attribute, sodass man weiß, dass `foo bar` immer ein Aufruf der Methode `bar` in dem Objekt, welches in der Variablen `foo` steckt bedeutet. Smalltalk hat es nicht nötig, Name der Methode (auch Selektor genannt) und Variable mit einem Punkt aneinanderzuketten. Man kann auch nicht "von außen" auf Exemplarvariablen zugreifen, was dann zu folgendem Boilerplate-Code in der Klasse `Foo`, die eine Exemplarvariable `bar` haben möge, führt:

Code: Alles auswählen

bar
    ^bar
    
bar: anObject
    bar := anObject
Das `^` ist der Return-Operator. Fehlt er, wird implizit `^self` ergänzt.

In Python gab es AFAIK zuerst Funktionen. Dann hat sich GvR überlegt, wie er mit dem, was er hatte, jetzt Objektorientierung einführen konnte und erkannt, dass das eigentlich nur ein paar Konventionen sind. `foo.bar()` ist eine Abkürzung für `foo_bar(foo)`, wobei jetzt `foo_bar` geeignet gewählt werden muss. Man stecke diese Funktion also in ein neues Objekt, das man Klasse nennt und schreibt dann `Foo.bar`. Will ich jetzt `bar` definieren, brauche ich einen Namen für den ersten (und in meinem Beispiel einzigen) Parameter und da fiel ihm wohl `self` als Konvention ein. Netter Nebeneffekt ist, dass `Foo.bar` eine (fast) ganz normale Funktion ist.

In Smalltalk kommt man normalerweise mit Methodenobjekten nicht direkt in Kontakt. Man kann zwar `Foo compiledMethodAt: #bar` benutzen, aber das ist bereits Metaprogrammierung und nicht mehr Teil des Sprachkerns. Auch müsste man selbst die Suche in Oberklassen durchführen.

Zwischen Smalltalk und Python lag noch die Sprache Self, die z.B. Vorbild für JavaScript war. Man hat hier versucht, die Smalltalk-Essenz zu finden und dabei Klassen als entbehrlich erkannt.

Self hat Objekte mit Slots, in denen (neben normalen Objekten) Methoden stecken können. Variablen irgendeiner Art gibt es keine. Auch keine Kontrollstrukturen oder andere Syntax außer `^`, welches jedoch nur noch ein nicht-lokales-Return in Blöcken ist. Ein Objekt `foo: (| bar. |)` hat einen Slot namens bar, den man mit `foo bar` abfragen und mit `foo bar: 1` setzen kann. `bar` und `bar:` sind nicht mehr wie in Smalltalk explizit zu schreibende Zugriffsfunktionen sondern primitive Slotzugriffe und damit eher mit Python-Attributen zu vergleichen. Im Gegensatz zu Python kann man sie aber nicht überschreiben. Will eine Methode `baz` des Objekts `foo` auf `bar` zugreifen, muss man (das implizit gebundene) `self` benutzen: `(| bar. baz = (self bar + 1) |)`. Würde man in der Methode nur `bar` benutzen, wäre es wie eine lokale Variable, nämlich ein Slot des aktiven Kontextobjekts, den man in jeder Methode mit dem leeren Empfänger (bei `foo bar` ist `foo` der Empfänger der Nachricht `bar`) ansprechen kann.

Das ist herrlich einfach und dennoch mächtig und extrem objektorientiert. So hätte man's in Python auch regeln können.

Dort hat man sich aber stattdessen dafür entschieden, lieber alles als Funktionen zu sehen und Objekte und Methoden als Speziallfall davon anzusehen. Ein bisschen so wie bei Lisp, wo objektorientierte Erweiterungen traditionell generische Funktionen sind, die über alle ihre Argumente zur richtigen Multimethode dispatchen. Betrachtet man nur das erste (explizite) Argument, dann funktioniert es so wie bei Python.

Stefan
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Montag 24. November 2008, 11:51

First-class Functions wären bei deinem "so hätte man's auch in Python machen können" vermutlich trotzdem noch (parallel) möglich gewesen, oder?

Die Slots-Geschichte steht nicht *zufällig* in Zusammenhang mit Pythons `__slots__`?
lunar

Montag 24. November 2008, 12:41

Y0Gi hat geschrieben:Die Slots-Geschichte steht nicht *zufällig* in Zusammenhang mit Pythons `__slots__`?
Eher nicht. Das, was sma da über Self-Slots erzählt hat, entspricht nicht dem, was __slots__ bei Python ist.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Montag 24. November 2008, 15:05

Nope, der Begriff wurde auch bei CLOS schon benutzt und wahrscheinlich noch früher und kam daher einfach bei beiden Sprachen unabhängig voneinander gelegen. Andererseits: Vielleicht fühlte sich GvR ja inspiriert?

Ich poste die Tage mal einen Self-Simulator in Python, den ich eben angefangen habe zum Spass zu schreiben... blöde Idee so einen Urlaubstag zu verbringen, ich weiß.

Stefan
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Dienstag 25. November 2008, 15:43

Moin,

ich selbst habe nichts gegen das explizite self. Nur, es geht um die Frage, ob man es nicht weglassen könnte und da muss man sagen: Natürlich ginge das. Andere Sprachen können das ja auch. Bei Python ginge das aber mit tiefgreifenden Änderungen (Compiler, Bibliotheken) einher und genau das will GvR nicht:
In the comments I saw some pretty extreme proposals to save Bruce's proposal, but generally at the cost of making the rules harder to follow, or requiring deeper changes elsewhere to the language
Klassen- und statische Funktionen sind Sonderfälle, die GvR aber nicht extra behandeln will (verständlich, aber das macht ein implizites self wohl unmöglich):
I reject hacks like special-casing '@classmethod' and '@staticmethod'.
Ob eine Funktion im Kontext einer Klasse (implizites cls innerhalb der Funktion) oder im Kontext einer Instanz (implizites self innerhalb der Funktion) aufgerufen wurde - die Info wäre da (wenn auch nicht zur Compile-Zeit) und könnte ausgewertet werden. Natürlich soll die Funktion nicht plötzlich ein zusätzliches Argument besitzen (wie GvR das angenommen hat), das keiner sieht, sondern ich denke eher in die Richtung eines zusätzlichen Schlüsselwortes (dann könnte ich auch eine Funktion außerhalb meiner Klasse definieren in später an sie ranpappen -> self wäre vorhanden). Dass man das nicht will, ist völlig ok: Warum die Sprache komplexer machen, wenn man mit den jetzigen Mitteln schon alles erreichen kann? Außerdem würde das nachträgliche Einführen der Schlüsselwörter self und cls so gut wie jeden Code brechen.

Das technische Geblubber mag momentan relevant sein, weil man die Technik nicht von heute auf morgen ändern kann, aber auf Dauer gesehen, könnte man die in den Griff kriegen.

Wie gesagt: Die Argumente sind für mich nicht besonders überzeugend. Explicit is better than implicit, limitierte Sprachbasis oder auch ganz banal: "das ist Pythons Inszenierung von OO", wie Leonidas so schön schrieb, sind für mich die einzig wirklichen Argumente.

Gruß,
Manuel
Qubit
User
Beiträge: 75
Registriert: Dienstag 7. Oktober 2008, 09:07

Dienstag 25. November 2008, 17:05

helduel hat geschrieben:Natürlich soll die Funktion nicht plötzlich ein zusätzliches Argument besitzen (wie GvR das angenommen hat), das keiner sieht, sondern ich denke eher in die Richtung eines zusätzlichen Schlüsselwortes (dann könnte ich auch eine Funktion außerhalb meiner Klasse definieren in später an sie ranpappen -> self wäre vorhanden). Dass man das nicht will, ist völlig ok
Hm, meinst du jetzt das hier..?

Code: Alles auswählen

>>> class Test: pass

>>> def test(self):
	print "test"

	
>>> Test.test=test
>>> del test
>>> t = Test()
>>> t.test()
test
>>> 
lunar

Dienstag 25. November 2008, 17:32

helduel hat geschrieben:Nur, es geht um die Frage, ob man es nicht weglassen könnte und da muss man sagen: Natürlich ginge das. Andere Sprachen können das ja auch.
Das sagt genau gar nichts aus. Es geht ja nicht darum, ob man die Referenz auf das Objekt selbst in anderen Sprachen implizieren kann, sondern ob das in Python möglich ist. Sicherlich kommen andere Sprachen wie C++ ohne eine explizite Angabe dieser Referenz aus. Dies allerdings auf Python zu übertragen und als Argument für die technische Möglichkeit anzuwenden, setzt voraus, dass diese Sprachen in ihrem Objektmodell Python gleichen.

Das ist aber definitiv nicht der Fall. C++ beispielsweise kommt zwar ohne explizites "this" aus, allerdings sind Funktionen und Methoden in C++ auch etwas fundamental verschiedenes. Auch kennt C++ keine Dekoratoren oder Methodenreferenzen. Daher treten die Probleme der Implikation, die bei Python immanent sind, unter C++ überhaupt nicht auf, weil die Sprache dies durch ihre Definition verhindert.

Bei Python ginge das aber mit tiefgreifenden Änderungen (Compiler, Bibliotheken) einher
Es ginge auch und vor allem mit größeren Änderungen am Objektmodell und der Sprachspezifikation einher, da man bei den erwähnten Problemfällen (vor allem bei Dekoratoren) genau definieren müsste, wann eine Methode oder Funktion eine implizite Referenz erhält. Man kann ja beispielsweise Funktionen einfach als Dekoratoren verwenden, genauso wie man Methoden außerhalb der Klassendeklaration ersetzen kann. Die daraus resultierenden Regeln wären mit Sicherheit komplex und stünden den Prinzipien von Python somit entgegen.
I reject hacks like special-casing '@classmethod' and '@staticmethod'.
Ob eine Funktion im Kontext einer Klasse (implizites cls innerhalb der Funktion) oder im Kontext einer Instanz (implizites self innerhalb der Funktion) aufgerufen wurde - die Info wäre da (wenn auch nicht zur Compile-Zeit) und könnte ausgewertet werden.
Wenn diese Information zur Laufzeit ausgewertet würde, stellt sich die Frage, was passiert, wenn keine Klassenmethode im Kontext einer Instanz aufgerufen wird.
Das technische Geblubber mag momentan relevant sein, weil man die Technik nicht von heute auf morgen ändern kann, aber auf Dauer gesehen, könnte man die in den Griff kriegen.
Ohne Änderungen am Objektmodell ist das nicht so einfach. Natürlich kann man das Objektmodell ändern – das von Java kommt ja auch ohne explizites self aus ;)
Antworten