Wie vermeide ich explizites 'Selbst' in Python?


130

Ich habe Python gelernt, indem ich einige Pygame-Tutorials befolgt habe .

Darin fand ich eine umfassende Verwendung des Schlüsselworts self , und da ich hauptsächlich aus Java stamme, vergesse ich immer wieder, self einzugeben . Zum Beispiel self.rect.centerxwürde ich anstelle von tippen rect.centerx, weil rect für mich bereits eine Mitgliedsvariable der Klasse ist.

Die Java parallel kann ich denke , für diese Situation mit allen Verweisen auf Membervariablen Präfix wird mit dieser .

Habe ich es nicht geschafft, alle Mitgliedsvariablen mit dem Präfix self zu versehen , oder gibt es eine Möglichkeit, sie zu deklarieren, die es mir ermöglicht, dies zu vermeiden?

Auch wenn das, was ich vorschlage, nicht pythonisch ist , würde ich gerne wissen, ob es möglich ist.

Ich habe mir diese verwandten SO-Fragen angesehen, aber sie beantworten nicht ganz, wonach ich suche:


5
Ich komme aus einem Java-Hintergrund und finde es natürlich, aber ich füge jedem Aufruf explizit "dies" hinzu, um klarer zu machen, dass ich mich auf eine Instanzvariable beziehe.
Uri

4
Kennen Sie die Konvention eines m_Präfixes für alle Mitgliedsnamen, die von einigen C ++ / Java-Programmierern beobachtet werden? Die Verwendung von self.hilft auf ähnliche Weise bei der Lesbarkeit. Außerdem sollten Sie dirtsimple.org/2004/12/python-is-not-java.html lesen .
Beni Cherniavsky-Paskin

2
Wird normalerweise m_nur für nicht öffentliche nicht statische Datenelemente verwendet (zumindest in C ++).

@ Beni großer verlinkter Artikel, und ja, ich folge tatsächlich der Konvention mVariableName, für Mitgliedsvariablen beim Codieren in Java zu verwenden. Ich denke, @ Anurags Kommentar fasst es ziemlich gut zusammen, was ein Java-Entwickler tun sollte, wenn er Python lernt.
Bguiz

11
Wie kommt es also, dass jeder OP sagt, warum es gut / notwendig / etc. Ist, sich selbst zu benutzen? aber niemand sagt, ob es irgendwie vermieden werden kann oder nicht? Auch wenn durch einen schmutzigen Trick?
Einpoklum

Antworten:


98

Python erfordert die Angabe von self. Das Ergebnis ist, dass es keine Verwirrung darüber gibt, was ein Mitglied ist und was nicht, auch ohne dass die vollständige Klassendefinition sichtbar ist. Dies führt zu nützlichen Eigenschaften wie: Sie können keine Mitglieder hinzufügen, die versehentlich Nichtmitglieder beschatten und dadurch den Code beschädigen.

Ein extremes Beispiel: Sie können eine Klasse schreiben, ohne zu wissen, welche Basisklassen sie haben könnte, und immer wissen, ob Sie auf ein Mitglied zugreifen oder nicht:

class A(some_function()):
  def f(self):
    self.member = 42
    self.method()

Das ist das Ganze Code! (some_function gibt den als Basis verwendeten Typ zurück.)

Eine andere, bei der die Methoden einer Klasse dynamisch zusammengesetzt sind:

class B(object):
  pass

print B()
# <__main__.B object at 0xb7e4082c>

def B_init(self):
  self.answer = 42
def B_str(self):
  return "<The answer is %s.>" % self.answer
# notice these functions require no knowledge of the actual class
# how hard are they to read and realize that "members" are used?

B.__init__ = B_init
B.__str__ = B_str

print B()
# <The answer is 42.>

Denken Sie daran, dass diese beiden Beispiele extrem sind und Sie sie nicht jeden Tag sehen werden. Ich schlage auch nicht vor, dass Sie häufig Code wie diesen schreiben sollten, aber sie zeigen deutlich, dass Aspekte des Selbst explizit erforderlich sind.


4
Danke dir. Diese Antwort trifft den Punkt, weil sie auch die Vorteile der Verwendung erklärt self.
Bguiz

2
@ Roger Pate: Bitte hör auf, meine Frage zu bearbeiten, um Python daraus zu entfernen. Ich denke, dass es dort hingehört. (und danke für die Antwort!)
Bguiz

3
@bguiz: Es ist SO üblich, die Tags im Titel nicht zu duplizieren. Als ich vor 2 Tagen bearbeitet habe, habe ich jedoch nicht gesehen, dass Sie den Titel vor 7 Monaten zurückgesetzt haben.

1
Es wäre schön, wenn sich das Selbst auf einen einzigen Charakter reduzieren könnte.
Dwjohnston

5
Das ist eigentlich ein ziemlich albern klingender Grund. Python akzeptiert möglicherweise keine Schatten, und Sie müssen dies ausdrücklich deklarieren, dh Ihr Schattenfeld mit einer Art von "segnen" __shadow__ myFieldName. Das würde auch ein versehentliches Abschatten verhindern, nicht wahr?
Einpoklum

37

Frühere Antworten sind im Grunde alle Varianten von "Sie können nicht" oder "Sie sollten nicht". Obwohl ich dem letzteren Gefühl zustimme, ist die Frage technisch immer noch unbeantwortet.

Darüber hinaus gibt es legitime Gründe, warum jemand etwas tun möchte, das der eigentlichen Frage entspricht. Eine Sache, auf die ich manchmal stoße, sind lange mathematische Gleichungen, bei denen die Verwendung langer Namen die Gleichung nicht wiedererkennbar macht. Hier sind einige Möglichkeiten, wie Sie dies in einem Beispiel aus der Dose tun können:

import numpy as np
class MyFunkyGaussian() :
    def __init__(self, A, x0, w, s, y0) :
        self.A = float(A)
        self.x0 = x0
        self.w = w
        self.y0 = y0
        self.s = s

    # The correct way, but subjectively less readable to some (like me) 
    def calc1(self, x) :
        return (self.A/(self.w*np.sqrt(np.pi))/(1+self.s*self.w**2/2)
                * np.exp( -(x-self.x0)**2/self.w**2)
                * (1+self.s*(x-self.x0)**2) + self.y0 )

    # The correct way if you really don't want to use 'self' in the calculations
    def calc2(self, x) :
        # Explicity copy variables
        A, x0, w, y0, s = self.A, self.x0, self.w, self.y0, self.s
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

    # Probably a bad idea...
    def calc3(self, x) :
        # Automatically copy every class vairable
        for k in self.__dict__ : exec(k+'= self.'+k)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

g = MyFunkyGaussian(2.0, 1.5, 3.0, 5.0, 0.0)
print(g.calc1(0.5))
print(g.calc2(0.5))
print(g.calc3(0.5))

Das dritte Beispiel - dh Verwenden for k in self.__dict__ : exec(k+'= self.'+k)ist im Grunde das, wonach die Frage tatsächlich fragt, aber lassen Sie mich klarstellen, dass ich es im Allgemeinen nicht für eine gute Idee halte.

Weitere Informationen und Möglichkeiten zum Durchlaufen von Klassenvariablen oder sogar Funktionen finden Sie in den Antworten und in der Diskussion zu dieser Frage . In diesem Blog-Beitrag erfahren Sie, wie Sie Variablen dynamisch benennen können und warum dies normalerweise keine gute Idee ist .

UPDATE: Es scheint keine Möglichkeit zu geben, lokale Elemente in einer Funktion in Python3 dynamisch zu aktualisieren oder zu ändern, sodass calc3 und ähnliche Varianten nicht mehr möglich sind. Die einzige Python3-kompatible Lösung, an die ich jetzt denken kann, ist die Verwendung von globals:

def calc4(self, x) :
        # Automatically copy every class variable in globals
        globals().update(self.__dict__)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

Was wiederum im Allgemeinen eine schreckliche Praxis wäre.


4
Brillant! Dies ist die (nur?) Richtigste Antwort. +1 Sie geben auch eindeutig einen praktischen Grund dafür an. + 1i
David Lotts

Nachdem ich eine Klasse erstellt und den Code darin verschoben habe: Jetzt werden nicht mehr alle Methoden und Variablen erkannt (kein Selbst ...). Ich werde an einen weiteren Grund erinnert, warum Python und ich nicht miteinander auskommen. Danke dafür als Idee. Es behebt das Problem / die Kopfschmerzen / die Unlesbarkeit insgesamt nicht, bietet jedoch eine bescheidene Problemumgehung.
Javadba

Warum nicht einfach aktualisieren localsstatt verwenden exec?
Nathan

Ich habe es locals().update(self.__dict__)in Python 2 und 3 versucht , aber es hat nicht funktioniert. In Python3 ist sogar der 'exec'-Trick keine Option mehr. Auf der anderen Seite globals().update(self.__dict__)funktioniert, wäre aber im Allgemeinen eine schreckliche Praxis.
argentum2f

26

Eigentlich selfist es kein Schlüsselwort, sondern nur der Name, der üblicherweise dem ersten Parameter von Instanzmethoden in Python gegeben wird. Und dieser erste Parameter kann nicht übersprungen werden, da dies der einzige Mechanismus ist, mit dem eine Methode weiß, auf welche Instanz Ihrer Klasse sie aufgerufen wird.


1
Diese Antwort, insbesondere der 2. Satz, ist für mich weitaus nützlicher als die akzeptierte Antwort, da ich wissen kann, dass "explizites Selbst" nur eine der Einschränkungen in Python ist und nicht vermeidbar ist
QuestionDriven

21

Sie können beispielsweise einen beliebigen Namen verwenden

class test(object):
    def function(this, variable):
        this.variable = variable

oder auch

class test(object):
    def function(s, variable):
        s.variable = variable

Sie müssen jedoch keinen Namen für den Bereich verwenden.

Ich empfehle Ihnen nicht, etwas anderes als sich selbst zu verwenden, es sei denn, Sie haben einen überzeugenden Grund, da dies für erfahrene Pythonisten fremd wäre.


26
Sie können dies tun, aber nicht ! Es gibt keinen Grund, Ihren Code seltsamer zu machen, als er sein muss. Ja, Sie können es alles nennen, aber die Konvention lautet, es zu nennen self, und Sie sollten der Konvention folgen. Dadurch wird Ihr Code für jeden erfahrenen Python-Programmierer, der ihn betrachtet, leichter verständlich. (Dies schließt ein, dass Sie in sechs Monaten versuchen herauszufinden, was Ihr altes Programm tut!)
steveha

3
Noch fremder:def function(_, variable): _.variable = variable
Bob Stein

1
@ BobStein-VisiBone noch fremder:def funcion(*args): args[0].variable = args[1]
Aemyl

4
@steveha er empfiehlt es nicht, diese Informationen sind sehr hilfreich für Leute wie mich, die nicht wussten, dass Sie andere Schlüsselwörter als self verwenden können und sich fragen, warum ein Objekt der eigenen Klasse übergeben wird.
Sven van den Boogaart

> "seltsamer". Python ist seltsam - besonders seine Klassenstrukturen und diese Verwendung von Selbst ist ein Kanarienvogel zur Unterstützung der Anti-Lesbarkeit. Ich weiß, dass danach viele Verteidiger kommen werden, aber das ändert nichts an der Wahrhaftigkeit.
Javadba

9

Ja, Sie müssen immer angeben self , da explizit nach der Python-Philosophie besser als implizit ist.

Sie werden auch feststellen, dass sich die Art und Weise, wie Sie in Python programmieren, stark von der Art und Weise unterscheidet, wie Sie in Java programmieren. Daher nimmt die Verwendung von selftendenziell ab, da Sie nicht alles innerhalb des Objekts projizieren. Vielmehr nutzen Sie Funktionen auf Modulebene, die besser getestet werden können.

Apropos. Ich hasste es zuerst, jetzt hasse ich das Gegenteil. Gleiches gilt für die eingedrückte Durchflussregelung.


2
"Sie nutzen die Funktion auf Modulebene, die besser getestet werden kann", ist zweifelhaft, und ich muss dem überhaupt nicht zustimmen. Es ist wahr, dass Sie nicht gezwungen sind, alles zu einer Methode (statisch oder nicht) einer Klasse zu machen, unabhängig davon, ob es sich um eine "logische Modulebene" handelt, aber das hat nichts mit sich selbst zu tun und hat keine wesentlichen Auswirkungen auf auf die eine oder andere Weise testen (für mich).

Es ergibt sich aus meiner Erfahrung. Ich folge ihm nicht jedes Mal als Mantra, aber es erleichtert das Testen, wenn Sie etwas, das keinen Zugriff auf Mitgliedsvariablen erfordert, als separate, unabhängige Methode verwenden. Ja, Sie trennen Logik von Daten, was ja gegen OOP ist, aber sie sind zusammen, nur auf Modulebene. Ich gebe dem einen oder anderen nicht die "beste" Note, es ist nur Geschmackssache. Manchmal spezifiziere ich Klassenmethoden, die nichts mit der Klasse selbst zu tun haben, da sie sich selfin keiner Weise berühren . Was bringt es also, sie in der Klasse zu haben?
Stefano Borini

Ich bin mit beiden Teilen nicht einverstanden (es scheint, dass ich "Nicht-Methoden" nicht öfter als in anderen Sprachen verwende), aber "was besser getestet werden kann" bedeutet, dass ein Weg dem anderen überlegen ist (genau so bin ich lese es? scheint nicht wahrscheinlich), obwohl ich meiner Erfahrung nach keine Unterstützung dafür finde. Beachten Sie, dass ich nicht sage, dass Sie immer die eine oder andere verwenden sollten, sondern nur, dass Methoden und Nicht-Methoden gleichermaßen getestet werden können.

5
Ja, natürlich können Sie, aber der Ansatz ist anders. Ein Objekt hat einen Status, eine Methode auf Modulebene nicht. Wenn Sie feststellen, dass ein Test beim Testen einer Methode auf Klassenebene fehlschlägt, können zwei Dinge falsch gewesen sein: 1) der Objektstatus zum Zeitpunkt des Aufrufs 2) die Methode selbst. Wenn Sie eine zustandslose Methode auf Modulebene haben, kann nur Fall 2 auftreten. Sie haben das Setup vom Objekt (wo es sich beim Test um eine Black Box handelt, da es von der eventuell komplexen Logik im Objekt gesteuert wird) in die Testsuite verschoben. Sie reduzieren die Komplexität und behalten die Kontrolle über das Setup.
Stefano Borini

1
"Wenn Sie eine zustandslose Methode auf Modulebene haben", was ist mit einer Stateful-Methode auf Modulebene? Sie sagen mir nur, dass zustandslose Funktionen einfacher zu testen sind als zustandsbehaftete, und ich stimme dem zu, aber es hat nichts mit Methoden oder Nicht-Methoden zu tun. Sehen Sie sich den self-Parameter genau so an: nur einen weiteren Parameter für die Funktion.

4

Das "Selbst" ist der herkömmliche Platzhalter der aktuellen Objektinstanz einer Klasse. Es wird verwendet, wenn Sie auf die Eigenschaft, das Feld oder die Methode des Objekts innerhalb einer Klasse verweisen möchten, als würden Sie auf "sich selbst" verweisen. Aber um es kürzer zu machen, hat jemand im Python-Programmierbereich angefangen, "self" zu verwenden, andere Bereiche verwenden "this", aber sie machen es als Schlüsselwort, das nicht ersetzt werden kann. Ich habe eher "its" verwendet, um die Lesbarkeit des Codes zu verbessern. Es ist eines der guten Dinge in Python - Sie haben die Freiheit, Ihren eigenen Platzhalter für die Instanz des Objekts zu wählen, außer "Selbst". Beispiel für sich selbst:

class UserAccount():    
    def __init__(self, user_type, username, password):
        self.user_type = user_type
        self.username = username            
        self.password = encrypt(password)        

    def get_password(self):
        return decrypt(self.password)

    def set_password(self, password):
        self.password = encrypt(password)

Jetzt ersetzen wir 'Selbst' durch 'Sein':

class UserAccount():    
    def __init__(its, user_type, username, password):
        its.user_type = user_type
        its.username = username            
        its.password = encrypt(password)        

    def get_password(its):
        return decrypt(its.password)

    def set_password(its, password):
        its.password = encrypt(password)

was ist jetzt besser lesbar?


warum nicht einfach s(oder ein anderer einzelner Buchstabe) stattits
javadba

'sein' hat eine Bedeutung und 's' nicht.
LEMUEL ADANE

shat die gleiche Bedeutung: ein Alias ​​für die Klasseninstanz. Ich müsste nachschlagen, was itsim Kontext
trotzdem

Beide sind für mich nicht lesbar. Es ist immer noch unlogisch, "sich selbst" als externen Parameter zu verwenden, es macht keinen Sinn.
Cesar

3

self ist Teil der Python-Syntax für den Zugriff auf Mitglieder von Objekten. Ich fürchte, Sie bleiben dabei


2
self ist eine Möglichkeit, den Zugriffsmodifikator zu erkennen, ohne ihn wirklich zu verwenden. +1
Perpetualcoder

1

Tatsächlich können Sie das Rezept "Implizites Selbst" aus der Präsentation von Armin Ronacher "5 Jahre schlechte Ideen" (google it) verwenden.

Es ist ein sehr cleveres Rezept, wie fast alles von Armin Ronacher, aber ich finde diese Idee nicht sehr ansprechend. Ich denke, ich würde es vorziehen, dies explizit zu tun in C # / Java.

Aktualisieren. Link zum "Rezept für schlechte Ideen": https://speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58


Ist dies der Link, auf den Sie sich beziehen, auf den Sie sich bezogen haben? Wenn ja, bitte in Ihre Antwort aufnehmen
Rubenjohn

Nein, Armin "schlechte Idee" sieht für meinen Geschmack lustiger aus. Ich habe einen Link eingefügt.
Alex Yu

Bevor Sie auf den Rezeptlink klicken, beachten Sie, dass dies implizit in der Parameterliste def method(<del> self </ del> enthalten ist, bei diesem cleveren Hack )jedoch self.variableweiterhin erforderlich ist.
David Lotts

0

Ja, Selbst ist langweilig. Aber ist es besser?

class Test:

    def __init__(_):
        _.test = 'test'

    def run(_):
        print _.test

10
_hat eine besondere Bedeutung in der Python-Shell, in der der zuletzt zurückgegebene Wert enthalten ist. Es ist sicher, es so zu verwenden, aber möglicherweise verwirrend; Ich würde es vermeiden.
Cairnarvon

Nein, das ist nicht besser, aber warum nicht stattdessen einen einzelnen Buchstaben, z. B. soder m(um C ++ nachzuahmen)
javadba

0

Von: Self Hell - Stateful Funktionen.

... ein hybrider Ansatz funktioniert am besten. Alle Ihre Klassenmethoden, die tatsächlich Berechnungen durchführen, sollten in Abschlüsse verschoben werden, und Erweiterungen zum Bereinigen der Syntax sollten in Klassen beibehalten werden. Füllen Sie die Verschlüsse in Klassen und behandeln Sie die Klasse wie einen Namespace. Die Verschlüsse sind im Wesentlichen statische Funktionen und erfordern daher kein Selbst *, auch nicht in der Klasse ...


Closures sind für kleine Anwendungsfälle in Ordnung, aber eine erweiterte Verwendung erhöht den Speicheraufwand eines Programms erheblich (da Sie prototypbasiertes OO anstelle von klassenbasiertem OO verwenden - daher benötigt jedes Objekt seine eigenen Funktionen lieber einen gemeinsamen Satz von Funktionen in einer Klasse behalten). Außerdem wird verhindert, dass Sie in der Lage sind, Methoden (z. B. __str__und dergleichen) zu zaubern / zu zerstören, da diese auf eine andere Weise als normale Methoden aufgerufen werden.
Dünen

0

Ich denke, dass es einfacher und lesbarer wäre, wenn es eine Anweisung "Mitglied" gäbe, genauso wie es "global" gibt, damit Sie dem Interpreter mitteilen können, welche Objekte Mitglieder der Klasse sind.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.