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.
bremer
User
Beiträge: 109
Registriert: Sonntag 25. Mai 2008, 00:13

Warum wurde die Sprache nicht so definiert, dass das:

Code: Alles auswählen

>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)
... so aussieht:

Code: Alles auswählen

>>> class Complex:
...     def __init__(realpart, imagpart):
...         r = realpart
...         i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)
Das ganze self, self, self verwirrt mich noch etwas.
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

bremer hat geschrieben: Das ganze self, self, self verwirrt mich noch etwas.
Vorneweg:
"self" ist kein Keyword, nur Konvention, ein beliebig wählbarer Bezeichner. Ein Bezeichner wofür? Für die Referenz auf ein Objekt, also Klasse, Instanz, usw., welche im Namensraum des Objekts selbst liegt.
Dass diese Referenz (standardmäßig) explizit adressiert wird, liegt im Objektdesign von Python. Hierdurch ist es zB möglich, extern Referenzen zu übergeben, wie zB, object.__getattribute__(ref,attr). Bei Zugriffen auf die Methoden von Klassen oder Instanzen wird diese Referenz von der Python Umgebung selbst gemanaged, also bei Klass.method(cls,..) oder Klass().method(self,..) sind die Referenzen 'cls' oder 'self' vorbelegt und müssen nicht angegeben werden, da Klass oder Klass() schon (im exterenen Namensraum) referenziert sind und somit auch im Namensraum des Objekts eben als "cls" oder "self" referenziert werden können.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Nicht alles was ich in einer Objektmethode deklariere, moechte ich automatisch als Klassenattibut haben:

Code: Alles auswählen

>>> class A:
...   def __init__(self):
...     tmp = ...
...     self.x = tmp/2 + ...
D.h. irgendeinen Mechanismus braucht man, um die lokalen Namen von den Objektattributen zu unterscheiden. Bei Java und C++ hat man z.B. ein this.x, was man allerdings bei den wenigsten anderen Sprachen hat, ist die Uebergabe des Objekts als ersten Parameter.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Desweiteren finde ich, dass das eine gute Konvention ist.
Gegenüber Sprachen wie C++ bleiben einem ja die Deklarationen erspart.
Ich fänd es gut wenn in C++ auch immer das this Objekt verwendet werden würde.
Aber man hat ja die Freiheit diesen wegzulassen und ich finde es ärgerlich in Fremdcode nachforschen zu müssen ob es ein Member ist oder nicht.
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Wie Rebecca schon richtig sagte, bedarf es einer Trennung zwischen lokalen Namen in Methoden und Klassen- oder Instanzattribute. In deiner zweiten Beispielklasse könnten `r` und `i` auch Klassenattribute sein.

In Java gibt es, wie bereits erwähnt, das Schlüsselwort `this`. Dies ist leider optional, so dass man immer genau hinsehen muss, woran ein Objekt nun unter diesem Namen gebunden wurde (oder ob es lokal ist). Python geht hier konsequent den "explicit is better than implicit"-Weg und man ist sofort im Bilde darüber, ob man auf die Instanz (`self`), die Klasse (`cls` oder der Klassenname) oder nur einen ungebundenen Namen zugreift.

In Ruby ist es ebenfalls so, dass man keinen Präfix für Methoden benötigt. So kann sich bspw. ein Aufruf von `show` im Konstruktor auf eine Methode der selben Klasse als auch (soweit ich weiß/annehme) auf eine außerhalb der Klasse definierte Funktion beziehen. In Python wäre der Unterschied deutlich, da im ersten Fall ein `self.` davor stünde.

Ruby verwendet weiterhin `@` und `@@` als Präfixe für Instanz- respektive Klassenattribute, denn die anfangs beschriebene Unterscheidung ist notwendig.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Y0Gi hat geschrieben:Ruby verwendet weiterhin `@` und `@@` als Präfixe für Instanz- respektive Klassenattribute, denn die anfangs beschriebene Unterscheidung ist notwendig.
Ja, das ist aber in Ruby auf syntaktischer Ebene gehandhabt und in Python ist ein Attributzugriff auf eine Instanz, nun ja, eben ein Attributzugriff und nichts weiter.

Was würde denn ohne so eine Aufteilung passieren? Deiner Argumentation nach würde jede lokale Variable jeder Funktion in der Klasse dann an die Instanz gebunden sein - ich denke nicht dass du so etwas haben wolltest, oder?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
bremer
User
Beiträge: 109
Registriert: Sonntag 25. Mai 2008, 00:13

Gut, das erklärt ja vieles.

Aber das erste self in der Klammern hätte man ja weglassen können.

def __init__(self, realpart, imagpart):
-->
def __init__(realpart, imagpart):

Stattdessen hätte man "self" als Keyword festschreiben können.
BlackJack

@bremer: Das würde die Sprache komplizierter machen. Methoden sind bei ihrer Definition noch Funktionen, die erst beim Abruf von einem Objekt zu einer gebundenen Methode werden. Man kann "Methoden" als Funktionen benutzen und jedes beliebige aufrufbare Objekt als Methode an eine Klasse binden. In das Konzept passt ein implizites `self` einfach nicht rein weil nicht in allen Fällen klar ist, an was `self` denn eigentlich gebunden werden soll.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

bremer hat geschrieben:Aber das erste self in der Klammern hätte man ja weglassen können

Code: Alles auswählen

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

Leonidas hat geschrieben:Ja, das ist aber in Ruby auf syntaktischer Ebene gehandhabt und in Python ist ein Attributzugriff auf eine Instanz, nun ja, eben ein Attributzugriff und nichts weiter.
Das ist ein wichtiger Unterschied, aber doch vollkommen okay. Wer die Anzahl an Schlüsselwörtern und Methoden für ähnliche Dinge - also quasi den TMTWWTDI-Faktor - vergleicht, wird feststellen, dass Python da offenbar mit einer simpleren, limitierteren Sprachbasis zu Werke geht - und eben so Dinge wie den Zugriff auf Klassen- und Instanzattribute mit Mitteln realisiert, die "schon da sind".
Leonidas hat geschrieben:Was würde denn ohne so eine Aufteilung passieren? Deiner Argumentation nach würde jede lokale Variable jeder Funktion in der Klasse dann an die Instanz gebunden sein - ich denke nicht dass du so etwas haben wolltest, oder?
Das bezieht sich vermutlich auf den OP?
fred.reichbier
User
Beiträge: 155
Registriert: Freitag 29. Dezember 2006, 18:27

Zu dem Thema vielleicht auch noch das hier, ich fand das interessant ;)
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Y0Gi hat geschrieben:Wer die Anzahl an Schlüsselwörtern und Methoden für ähnliche Dinge - also quasi den TMTWWTDI-Faktor - vergleicht, wird feststellen, dass Python da offenbar mit einer simpleren, limitierteren Sprachbasis zu Werke geht - und eben so Dinge wie den Zugriff auf Klassen- und Instanzattribute mit Mitteln realisiert, die "schon da sind"
IMHO das einzig wirkliche Argument, das für das explizite self spricht.

Was Guide van Rossum dazu erklärt hat, hat mich überhaupt nicht überzeugt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

helduel hat geschrieben:
Y0Gi hat geschrieben:Wer die Anzahl an Schlüsselwörtern und Methoden für ähnliche Dinge - also quasi den TMTWWTDI-Faktor - vergleicht, wird feststellen, dass Python da offenbar mit einer simpleren, limitierteren Sprachbasis zu Werke geht - und eben so Dinge wie den Zugriff auf Klassen- und Instanzattribute mit Mitteln realisiert, die "schon da sind"
IMHO das einzig wirkliche Argument, das für das explizite self spricht.
Lies dir mal murphys Erklärung durch.

@Y0Gi: Ja.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
lunar

helduel hat geschrieben:Was Guide van Rossum dazu erklärt hat, hat mich überhaupt nicht überzeugt.
Darf man fragen, warum, bzw. wie du die Punkte – insbesondere die über Dekoratoren und ihr Zusammenspiel mit self – entkräften würdest?
BlackJack

@helduel: Die Probleme. die entstehen wenn man Funktionen nicht in Klassen definiert oder innerhalb von anderen Funktionen/Methoden oder bei Dekoratoren, sind keine Argumente?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

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

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

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

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

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
Antworten