Zugriff auf Mitgliedsvariablen einer Klasse in Python?

83
class Example(object):
    def the_example(self):
        itsProblem = "problem"

theExample = Example()
print(theExample.itsProblem)

Wie greife ich auf die Variable einer Klasse zu? Ich habe versucht, diese Definition hinzuzufügen:

def return_itsProblem(self):
    return itsProblem

Das scheitert aber auch.

Adam
quelle
1
Titel bearbeitet, Frage tatsächlich über Klasse "statische" Variablen: stackoverflow.com/questions/707380/…
Ciro Santilli 28 冠状 病 六四 六四 法轮功

Antworten:

139

Die Antwort in wenigen Worten

In Ihrem Beispiel itsProblemhandelt es sich um eine lokale Variable.

Sie müssen verwenden self, um Instanzvariablen festzulegen und abzurufen. Sie können es in der __init__Methode einstellen . Dann wäre Ihr Code:

class Example(object):
    def __init__(self):
        self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

Wenn Sie jedoch eine echte Klassenvariable wünschen, verwenden Sie den Klassennamen direkt:

class Example(object):
    itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)
print (Example.itsProblem)

Aber seien Sie vorsichtig mit dieser, da sie theExample.itsProblemautomatisch gleich gesetzt wird Example.itsProblem, aber überhaupt nicht dieselbe Variable ist und unabhängig geändert werden kann.

Einige Erklärungen

In Python können Variablen dynamisch erstellt werden. Daher können Sie Folgendes tun:

class Example(object):
    pass

Example.itsProblem = "problem"

e = Example()
e.itsSecondProblem = "problem"

print Example.itsProblem == e.itsSecondProblem 

druckt

Wahr

Genau das machen Sie also mit den vorherigen Beispielen.

In Python verwenden wir zwar selfals this, aber es ist ein bisschen mehr als das. selfist das erste Argument für eine Objektmethode, da das erste Argument immer die Objektreferenz ist. Dies erfolgt automatisch, unabhängig davon, ob Sie es anrufen selfoder nicht.

Was bedeutet, dass Sie Folgendes tun können:

class Example(object):
    def __init__(self):
        self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

oder:

class Example(object):
    def __init__(my_super_self):
        my_super_self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

Es ist genau das gleiche. Das erste Argument einer beliebigen Objektmethode ist das aktuelle Objekt. Wir nennen es nur selfeine Konvention. Und Sie fügen diesem Objekt nur eine Variable hinzu, so wie Sie es von außen tun würden.

Nun zu den Klassenvariablen.

Wenn Sie das tun:

class Example(object):
    itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

Sie werden feststellen, dass wir zuerst eine Klassenvariable festlegen und dann auf eine Objektvariable (Instanzvariable) zugreifen . Wir haben diese Objektvariable nie gesetzt, aber sie funktioniert. Wie ist das möglich?

Nun, Python versucht zuerst, die Objektvariable abzurufen, aber wenn es sie nicht finden kann, erhalten Sie die Klassenvariable. Warnung: Die Klassenvariable wird von Instanzen gemeinsam genutzt und die Objektvariable nicht.

Verwenden Sie daher niemals Klassenvariablen, um Standardwerte für Objektvariablen festzulegen. Verwenden Sie __init__dafür.

Schließlich werden Sie feststellen, dass Python-Klassen Instanzen und damit Objekte selbst sind, was neue Erkenntnisse zum Verständnis der oben genannten gibt. Komm zurück und lies das später noch einmal, sobald du das merkst.

e-satis
quelle
Sie sagen: "theExample.itsProblem wird automatisch auf Example.itsProblem gesetzt, ist aber überhaupt nicht dieselbe Variable und kann unabhängig geändert werden" - aber das ist nicht ganz richtig und Ihre Formulierung ist irreführend. Ich vertraue darauf, dass Sie wissen, was dort vor sich geht, und schlage daher vor, Folgendes umzuformulieren: "Es ist dieselbe Variable, aber sie kann für jedes Objekt unabhängig neu gebunden werden."
Jsbueno
Ja, aber Bindung ist ein Konzept, das jemand für eine andere Programmiersprache wie Java oder C (wie ich vermute, das OP ist) völlig unbekannt ist. Dann würde ich erklären, was Bindung ist, dann späte Bindung, dann das Problem mit Referenzen auf veränderlichen Objekten. Es wäre zu lang. Ich denke, irgendwann muss man Präzision auf dem Altar des Verstehens opfern.
E-Satis
Es gibt auch (ich denke, das könnte 2.7+ sein) den @ classmethod-Dekorateur, der einen Blick wert ist. interessante Diskussion hier - stackoverflow.com/questions/12179271/…
jsh
1
Namensbindung: Ich denke, es ist eine schlechte Idee, falsche Aussagen zu machen, egal auf welcher Ebene. Ich schlage diese geringfügige Änderung vor: Aber seien Sie vorsichtig mit dieser, da sie theExample.itsProblemautomatisch gleich eingestellt wird Example.itsProblem, aber aus praktischer Sicht * überhaupt nicht dieselbe Variable ist und unabhängig geändert werden kann. *: Tatsächlich beginnt es als das gleiche Objekt, aber es ist sehr leicht versehentlich das ändern , wenn Sie Python nicht verstehen Name-Bindung
n611x007
11

Sie deklarieren eine lokale Variable, keine Klassenvariable. Verwenden Sie zum Festlegen einer Instanzvariablen (Attribut)

class Example(object):
    def the_example(self):
        self.itsProblem = "problem"  # <-- remember the 'self.'

theExample = Example()
theExample.the_example()
print(theExample.itsProblem)

Verwenden Sie zum Festlegen einer Klassenvariablen (auch als statisches Element bezeichnet)

class Example(object):
    def the_example(self):
        Example.itsProblem = "problem"
        # or, type(self).itsProblem = "problem"
        # depending what you want to do when the class is derived.
kennytm
quelle
1
Nur dass Klassenvariablen auf Klassenebene einmal deklariert werden . Ihr Weg wird es bei jeder Instanziierung zurücksetzen, das ist wirklich nicht das, was Sie wollen. Siehe auch stackoverflow.com/questions/2709821/python-self-explained für die Gründe für explizites Selbst.
2

Wenn Sie eine Instanzfunktion haben (dh eine, die selbst übergeben wird), können Sie self verwenden, um mithilfe von einen Verweis auf die Klasse abzurufen self.__class__

Im folgenden Code erstellt Tornado beispielsweise eine Instanz für die Verarbeitung von get_handlerAbrufanforderungen. Wir können jedoch die Klasse abrufen und damit einen Riak-Client speichern, sodass wir nicht für jede Anforderung eine erstellen müssen.

import tornado.web
import riak

class get_handler(tornado.web.requestHandler):
    riak_client = None

    def post(self):
        cls = self.__class__
        if cls.riak_client is None:
            cls.riak_client = riak.RiakClient(pb_port=8087, protocol='pbc')
        # Additional code to send response to the request ...
    
Andrew Pate
quelle
Dies ist ein sehr gutes Beispiel, um die Frage aus dem Originaltitel des OP zu demonstrieren. Tatsächlich stimmt das OP-Beispiel nicht mit dem Titel überein, und andere Antworten beziehen sich auf das Beispiel. Auf jeden Fall ist dies der beste Weg, um auf eine Variable auf Klassenebene zuzugreifen, da sie nicht gegen das DRY-Prinzip verstößt. Wie einige der anderen Beispiele. Es ist besser, self .__ class__ zu verwenden, als den Klassennamen zu wiederholen. Dies macht den Code zukunftssicher, erleichtert das Refactoring und wenn Sie es wagen, Unterklassen zu verwenden, kann dies auch Vorteile haben.
Mit
Leser sollten gewarnt werden, dass die Verwendung eines Handlers wie oben zu Problemen führen kann, nicht nur bei Anwendungen ohne Singlethread. Mehrere Instanzen der Klasse könnten theoretisch denselben Handler parallel verwenden, und wenn der Handler nicht dafür ausgelegt ist, was von seiner internen Struktur abhängt, funktioniert er möglicherweise nicht. 'Parallel' in einer einzelnen Thread-Anwendung bedeutet, dass die Instanz der ersten Klasse die Verwendung des Handlers noch nicht beendet hat und eine zweite Instanz die Verwendung des Handlers beginnt. In solchen Fällen kann empfohlen werden, stattdessen eine Instanzvariable zu verwenden.
Mit
0

Implementieren Sie die return-Anweisung wie im folgenden Beispiel! Du solltest gut sein. Ich hoffe es hilft jemandem ..

class Example(object):
    def the_example(self):
        itsProblem = "problem"
        return itsProblem 


theExample = Example()
print theExample.the_example()
Bürgermeister
quelle
1
Sie sollten Ihren Codeeinzug korrigieren. Es gibt auch überlegene Antworten auf diese Frage und Ihre Antwort ist eine grundlegende Variation dieser Antworten, keine alternative Lösung ...
HEADLESS_0NE