Warum muss das Argument "self" in einer Python-Methode explizit vorhanden sein?

196

Wenn Sie eine Methode für eine Klasse in Python definieren, sieht sie ungefähr so ​​aus:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

In einigen anderen Sprachen, wie z. B. C #, haben Sie jedoch einen Verweis auf das Objekt, an das die Methode mit dem Schlüsselwort "this" gebunden ist, ohne es als Argument im Methodenprototyp zu deklarieren.

War dies eine absichtliche Entscheidung zum Sprachdesign in Python oder gibt es einige Implementierungsdetails, die die Übergabe von "self" als Argument erfordern?

Schreibgeschützt
quelle
15
Ich wette, Sie wären auch daran interessiert zu wissen, warum Sie explizit schreiben müssen, um selfauf Mitglieder zuzugreifen - stackoverflow.com/questions/910020/…
Piotr Dobrogost
1
Aber es sieht irgendwie nach einer
Kesselplatte aus
Ein bisschen verwirrend, aber es lohnt sich zu verstehen stackoverflow.com/a/31367197/1815624
CrandellWS

Antworten:

91

Ich zitiere gerne Peters 'Zen of Python. "Explizit ist besser als implizit."

In Java und C ++ kann ' this.' abgeleitet werden, außer wenn Sie Variablennamen haben, die das Ableiten unmöglich machen. Also braucht man es manchmal und manchmal nicht.

Python entscheidet sich dafür, solche Dinge explizit zu machen, anstatt auf einer Regel zu basieren.

Da nichts impliziert oder angenommen wird, werden außerdem Teile der Implementierung verfügbar gemacht. self.__class__, self.__dict__Und andere „interne“ Strukturen sind in nahe liegender Weise zur Verfügung.

S.Lott
quelle
53
Obwohl es schön wäre, eine weniger kryptische Fehlermeldung zu haben, wenn Sie es vergessen.
Martin Beckett
9
Wenn Sie jedoch eine Methode aufrufen, bei der Sie keine Objektvariable übergeben müssen, verstößt dies nicht gegen die explizite Regel? Wenn Sie diesen Zen behalten möchten, muss er ungefähr so ​​lauten: object.method (object, param1, param2). Sieht irgendwie inkonsistent aus ...
Vedmant
10
"explizit ist besser als implizit" - Ist der "Stil" von Python nicht auf impliziten Dingen aufgebaut? zB implizite Datentypen, implizite Funktionsgrenzen (keine {}), impliziter Variablenbereich ... Wenn globale Variablen in Modulen in Funktionen verfügbar sind ... warum sollte diese Logik / Argumentation nicht auf Klassen angewendet werden? Wäre die vereinfachte Regel nicht einfach "alles, was auf einer höheren Ebene deklariert wurde, ist auf einer niedrigeren Ebene verfügbar", wie durch Einrückung bestimmt?
Simon
13
"Explizit ist besser als implizit" Unsinn entdeckt
Vahid Amiri
10
Seien wir ehrlich, es ist einfach schlecht. Dafür gibt es keine Entschuldigung. Es ist nur ein hässliches Relikt, aber es ist in Ordnung.
Toskan
62

Es geht darum, den Unterschied zwischen Methoden und Funktionen zu minimieren. Sie können auf einfache Weise Methoden in Metaklassen generieren oder Methoden zur Laufzeit zu bereits vorhandenen Klassen hinzufügen.

z.B

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

Es erleichtert auch (soweit ich weiß) die Implementierung der Python-Laufzeit.

Ryan
quelle
10
+1 für Damit wird der Unterschied zwischen Methoden und Funktionen minimiert. Diese Antwort sollte akzeptiert werden
Benutzer
Dies ist auch die Wurzel von Guidos viel verknüpfter Erklärung.
Marcin
1
Dies zeigt auch, dass in Python, wenn Sie c.bar () ausführen, zuerst die Instanz auf Attribute und dann die Klassenattribute überprüft werden. Sie können also jederzeit Daten oder Funktionen (Objekte) an eine Klasse 'anhängen' und erwarten, dass sie in ihrer Instanz zugreifen (dh dir (Instanz) wird es so machen). Nicht nur, wenn Sie eine c-Instanz "erstellt" haben. Es ist sehr dynamisch.
Nishant
10
Ich kaufe es nicht wirklich. Selbst in den Fällen, in denen Sie die übergeordnete Klasse benötigen, können Sie sie bei der Ausführung ableiten. Und die Äquivalenz zwischen Instanzmethoden und Klassenfunktionen, die Instanzen übergeben haben, ist albern. Ruby kommt ohne sie gut aus.
Zachaysan
2
Mit JavaScript können Sie einem Objekt zur Laufzeit Methoden hinzufügen, was selfin der Funktionsdeklaration nicht erforderlich ist (wohlgemerkt, dies wirft möglicherweise Steine ​​aus einem Glashaus, da JavaScript eine ziemlich knifflige thisBindungssemantik aufweist)
Jonathan Benn
55

Ich schlage vor, dass man Guido van Rossums Blog zu diesem Thema lesen sollte - Warum explizites Selbst bleiben muss .

Wenn eine Methodendefinition dekoriert ist, wissen wir nicht, ob wir ihr automatisch einen 'self'-Parameter geben sollen oder nicht: Der Decorator könnte die Funktion in eine statische Methode (die kein' self 'hat) oder eine Klassenmethode (welche) umwandeln hat eine lustige Art von Selbst, die sich auf eine Klasse anstatt auf eine Instanz bezieht), oder es könnte etwas völlig anderes tun (es ist trivial, einen Dekorator zu schreiben, der '@classmethod' oder '@staticmethod' in reinem Python implementiert). Es gibt keine Möglichkeit, ohne zu wissen, was der Dekorateur tut, um die zu definierende Methode mit einem impliziten 'Selbst'-Argument auszustatten oder nicht.

Ich lehne Hacks wie '@classmethod' und '@staticmethod' ab.

Bhadra
quelle
16

Python zwingt Sie nicht dazu, "self" zu verwenden. Sie können ihm einen beliebigen Namen geben. Sie müssen sich nur daran erinnern, dass das erste Argument in einem Methodendefinitionsheader eine Referenz auf das Objekt ist.

Victor Noagbodji
quelle
Konventionell sollte es jedoch "self" für Fälle oder "cls" sein, in denen Typen beteiligt sind (mmmm Metaklassen)
pobk
1
Es zwingt dazu, sich bei jeder Methode als ersten Parameter zu setzen, nur zusätzlichen Text, der für mich nicht viel Sinn macht. Andere Sprachen funktionieren damit einwandfrei.
Vedmant
habe ich recht ? immer der erste Parameter ist eine Referenz auf das Objekt.
Mohammad Mahdi KouchakYazdi
@MMKY Nein, zum Beispiel @staticmethoddamit nicht.
Mark
1
"Man muss sich nur daran erinnern, dass das erste Argument in einer Methodendefinition ..." Ich experimentierte damit, das Wort "self" in "kwyjibo" zu ändern, und es funktionierte immer noch. Wie oft erklärt wird, ist nicht das Wort "Selbst" wichtig, sondern die Position dessen, was diesen Raum einnimmt (?)
RBV
7

Außerdem können Sie dies tun: (Kurz gesagt, das Aufrufen Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)gibt 12 zurück, dies jedoch auf die verrückteste Art und Weise.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

In Sprachen wie Java und C # ist dies natürlich schwerer vorstellbar. Indem Sie die Selbstreferenz explizit machen, können Sie durch diese Selbstreferenz auf jedes Objekt verweisen. Außerdem ist eine solche Art, zur Laufzeit mit Klassen zu spielen, in den statischeren Sprachen schwieriger - nicht, dass es notwendigerweise gut oder schlecht ist. Es ist nur so, dass das explizite Selbst all diese Verrücktheit existieren lässt.

Stellen Sie sich außerdem Folgendes vor: Wir möchten das Verhalten von Methoden anpassen (für die Profilerstellung oder für verrückte schwarze Magie). Dies kann uns zum Nachdenken bringen: Was wäre, wenn wir eine Klasse hätten, Methodderen Verhalten wir überschreiben oder kontrollieren könnten?

Nun hier ist es:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

Und jetzt: InnocentClass().magic_method()wird sich wie erwartet verhalten. Die Methode wird mit dem innocent_selfParameter an InnocentClassund mit magic_selfder MagicMethod-Instanz gebunden. Seltsam, oder? Es ist wie mit 2 Schlüsselwörtern this1und this2in Sprachen wie Java und C #. Magie wie diese ermöglicht es Frameworks, Dinge zu tun, die sonst viel ausführlicher wären.

Auch hier möchte ich die Ethik dieses Materials nicht kommentieren. Ich wollte nur Dinge zeigen, die ohne eine explizite Selbstreferenz schwieriger zu machen wären.

vlad-ardelean
quelle
4
Wenn ich Ihr erstes Beispiel betrachte, kann ich dasselbe in Java tun: Die innere Klasse muss aufrufen OuterClass.this, um das 'Selbst' von der äußeren Klasse zu erhalten, aber Sie können es trotzdem thisals Referenz für sich selbst verwenden. sehr ähnlich zu dem, was Sie hier in Python tun. Für mich war das nicht schwerer vorstellbar. Vielleicht hängt es von den Kenntnissen der betreffenden Sprache ab?
Klaar
Aber können Sie trotzdem auf Bereiche verweisen, wenn Sie sich in einer Methode einer anonymen Klasse befinden, die in einer anonymen Klasse definiert ist, die in einer anonymen Implementierung der Schnittstelle definiert ist Something, die wiederum in einer weiteren anonymen Implementierung von definiert ist Something? In Python können Sie sich natürlich auf jeden Bereich beziehen.
Vlad-Ardelean
Sie haben Recht, in Java können Sie nur auf die äußere Klasse verweisen, indem Sie ihren expliziten Klassennamen aufrufen und diesen als Präfix verwenden this. Implizite Referenzen sind in Java unmöglich.
Klaar
Ich frage mich, ob dies funktionieren würde: In jedem Bereich (jeder Methode) gibt es eine lokale Variable, die auf das thisErgebnis verweist . Zum Beispiel Object self1 = this;(verwenden Sie entweder Object oder etwas weniger Generisches). Dann, wenn Sie Zugriff auf die Variable in den höheren Bereichen haben, könnten Sie haben Zugang zu self1, self2... selfn. Ich denke, diese sollten für endgültig erklärt werden oder so, aber es könnte funktionieren.
Vlad-Ardelean
3

Ich denke, der wahre Grund neben "The Zen of Python" ist, dass Funktionen in Python erstklassige Bürger sind.

Was sie im Wesentlichen zu einem Objekt macht. Das grundlegende Problem ist nun, wenn Ihre Funktionen auch Objekte sind, wie würden Sie im objektorientierten Paradigma Nachrichten an Objekte senden, wenn die Nachrichten selbst Objekte sind?

Sieht aus wie ein Hühnerei-Problem. Um dieses Paradoxon zu verringern, besteht die einzige Möglichkeit darin, entweder einen Ausführungskontext an Methoden zu übergeben oder ihn zu erkennen. Da Python jedoch verschachtelte Funktionen haben kann, ist dies nicht möglich, da sich der Ausführungskontext für innere Funktionen ändern würde.

Dies bedeutet, dass die einzig mögliche Lösung darin besteht, 'self' (den Kontext der Ausführung) explizit zu übergeben.

Ich glaube, es ist ein Implementierungsproblem, das der Zen viel später hatte.

pankajdoharey
quelle
Hallo, ich bin neu in Python (aus dem Java-Hintergrund) und habe nicht ganz verstanden, was Sie gesagt haben: "Wie würden Sie Nachrichten an Objekte senden, wenn die Nachrichten selbst Objekte sind?". Warum ist das ein Problem, können Sie näher darauf eingehen?
Qiulang
1
@Qiulang Aah entspricht in der objektorientierten Programmierung das Aufrufen von Methoden für Objekte dem Versenden von Nachrichten an Objekte mit oder ohne Nutzlast (Parameter für Ihre Funktion). Interne Methoden würden als Codeblock dargestellt, der einer Klasse / einem Objekt zugeordnet ist, und verwenden die implizite Umgebung, die ihr über das Objekt zur Verfügung steht, für das sie aufgerufen wird. Wenn Ihre Methoden jedoch Objekte sind, können sie unabhängig davon existieren, ob sie einer Klasse / einem Objekt zugeordnet sind, was die Frage aufwirft, ob Sie diese Methode aufrufen, für welche Umgebung würde sie ausgeführt werden?
Pankajdoharey
Es muss also einen Mechanismus geben, um eine Umgebung bereitzustellen. Selbst würde die aktuelle Umgebung zum Zeitpunkt der Ausführung bedeuten, aber Sie könnten auch eine andere Umgebung bereitstellen.
Pankajdoharey
2

Ich denke, das hat mit PEP 227 zu tun:

Auf Namen im Klassenbereich kann nicht zugegriffen werden. Namen werden im innersten umschließenden Funktionsbereich aufgelöst. Wenn eine Klassendefinition in einer Kette verschachtelter Bereiche auftritt, überspringt der Auflösungsprozess Klassendefinitionen. Diese Regel verhindert ungerade Interaktionen zwischen Klassenattributen und lokalem Variablenzugriff. Wenn in einer Klassendefinition eine Namensbindungsoperation auftritt, wird ein Attribut für das resultierende Klassenobjekt erstellt. Um auf diese Variable in einer Methode oder in einer in einer Methode verschachtelten Funktion zuzugreifen, muss eine Attributreferenz entweder über self oder über den Klassennamen verwendet werden.

daole
quelle
1

Wie in Python selbst erklärt , entmystifiziert

alles wie obj.meth (args) wird zu Class.meth (obj, args). Der aufrufende Prozess ist automatisch, während der empfangende Prozess nicht (explizit) ist. Aus diesem Grund muss der erste Parameter einer Funktion in der Klasse das Objekt selbst sein.

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from origin"""
        return (self.x**2 + self.y**2) ** 0.5

Anrufungen:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init () definiert drei Parameter, aber wir haben gerade zwei (6 und 8) übergeben. In ähnlicher Weise erfordert distance () eins, aber es wurden keine Argumente übergeben.

Warum beschwert sich Python nicht über diese Nichtübereinstimmung der Argumentnummer ?

Wenn wir eine Methode mit einigen Argumenten aufrufen, wird im Allgemeinen die entsprechende Klassenfunktion aufgerufen, indem das Objekt der Methode vor dem ersten Argument platziert wird. Also wird alles wie obj.meth (args) zu Class.meth (obj, args). Der aufrufende Prozess ist automatisch, während der empfangende Prozess nicht (explizit) ist.

Aus diesem Grund muss der erste Parameter einer Funktion in der Klasse das Objekt selbst sein. Das Schreiben dieses Parameters als Selbst ist lediglich eine Konvention . Es ist kein Schlüsselwort und hat in Python keine besondere Bedeutung. Wir könnten andere Namen (wie diesen) verwenden, aber ich empfehle Ihnen dringend, dies nicht zu tun. Die Verwendung anderer Namen als self wird von den meisten Entwicklern missbilligt und beeinträchtigt die Lesbarkeit des Codes ("Lesbarkeit zählt").
...
Im ersten Beispiel ist self.x ein Instanzattribut, während x eine lokale Variable ist. Sie sind nicht gleich und liegen in verschiedenen Namespaces.

Selbst ist hier, um zu bleiben

Viele haben vorgeschlagen, sich in Python zu einem Schlüsselwort zu machen, wie dies in C ++ und Java der Fall ist. Dies würde die redundante Verwendung von explizitem Selbst aus der formalen Parameterliste in Methoden eliminieren. Diese Idee scheint zwar vielversprechend, wird aber nicht passieren. Zumindest nicht in naher Zukunft. Der Hauptgrund ist die Abwärtskompatibilität . Hier ist ein Blog vom Schöpfer von Python selbst, in dem erklärt wird, warum das explizite Selbst bleiben muss.

mon
quelle
-5

Es gibt noch eine andere sehr einfache Antwort: Laut dem Zen von Python ist "explizit besser als implizit".

Flávio Amieiro
quelle