Ich lerne gerade Python und komme aus einem C-Hintergrund, also lassen Sie es mich wissen, wenn ich Verwirrung/Verwechslung zwischen beiden habe.
Angenommen, ich habe folgende Klasse:
class Node(object):
def __init__(self, element):
self.element = element
self.left = self.right = None
@classmethod
def tree(cls, element, left, right):
node = cls(element)
node.left = left
node.right = right
return node
Dies ist eine Klasse mit dem Namen Node
, die den Konstruktor überlastet, um bei Bedarf andere Argumente verarbeiten zu können.
Was ist der Unterschied zwischen der Definition von self.element
in __init__
(wie oben gezeigt) und nicht wie folgt:
class Node(object):
element, left, right = None
def __init__(self, element):
self.element = element
self.left = self.right = None
Ist self.element
in __init__
nicht identisch mit der definierten Variablen element
der Klasse? Würde das nicht einfach element
von None
auf den element
-Wert überschreiben, der in __init__
übergeben wurde?
Eines ist ein Klassenattribut, während das andere ein Instanzattribut ist. Sie sind unterschiedlich, aber sie sind eng miteinander verbunden, so dass sie manchmal gleich aussehen.
Es hat damit zu tun, wie Python Attribute nachschlägt. Es gibt eine Hierarchie. In einfachen Fällen könnte es so aussehen:
instance -> Subclass -> Superclass -> object (built-in type)
Wenn Sie in instance
nach einem Attribut wie diesem suchen ...
`instance.val`
... was tatsächlich passiert ist, dass zuerst , Python in der Instanz selbst nach val
sucht. Wenn val
dann nicht gefunden wird, sieht es in seiner Klasse Subclass
aus. Wenn dort val
nicht gefunden wird, wird im übergeordneten Element von Subclass
, Superclass
gesucht. Dies bedeutet, dass wenn Sie dies tun ...
>>> class Foo():
foovar = 10
def __init__(self, val):
self.selfvar = val
... alle Instanzen von Foo
share foovar
, aber ihre eigenen selfvar
s. Hier ist ein einfaches, konkretes Beispiel, wie das funktioniert:
>>> f = Foo(5)
>>> f.foovar
10
>>> Foo.foovar
10
Wenn wir foovar
nicht berühren, ist es für f
und Foo
gleich. Aber wenn wir f.foovar
... ändern.
>>> f.foovar = 5
>>> f.foovar
5
>>> Foo.foovar
10
... fügen wir ein Instanzattribut hinzu, das den Wert von Foo.foovar
effektiv maskiert. Wenn wir Foo.foovar
direkt ändern, hat dies keine Auswirkungen auf unsere foo
-Instanz:
>>> Foo.foovar = 7
>>> f.foovar
5
Es wirkt sich jedoch auf eine neue foo
-Instanz aus:
>>> Foo(5).foovar
7
Denken Sie auch daran, dass veränderliche Objekte eine weitere Ebene der Indirektion hinzufügen (wie mgilson mich daran erinnert). Hier bezieht sich f.foovar
auf dasselbe Objekt wie Foo.foovar
. Wenn Sie also das Objekt ändern, werden die Änderungen in der Hierarchie weitergegeben:
>>> Foo.foovar = [1]
>>> f = Foo(5)
>>> f.foovar[0] = 99
>>> Foo.foovar
[99]
In Python können Klassenvariablen und Instanzvariablen mit demselben Namen verwendet werden. Sie befinden sich separat im Speicher und werden ganz unterschiedlich abgerufen.
In Ihrem Code:
class Node(object):
element, left, right = None
def __init__(self, element):
self.element = element
self.left = self.right = None
Die ersten Variablen (außerhalb der __init__
-Funktion) werden Klassenvariablen genannt. Auf diese kann anschließend mit Node.element
usw. zugegriffen werden. Diese entsprechen statischen Member-Variablen in C++ und werden von allen Instanzen der Klasse gemeinsam verwendet.
Der zweite Variablensatz (innerhalb der __init__
-Funktion) wird als Instanzvariable bezeichnet. Auf diese wird über das self
-Objekt zugegriffen, z. self.element
oder durch den Instanznamen, z. myNode.element
außerhalb der Klasse.
Es ist wichtig zu beachten, dass Sie entweder das Formular self.variable
oder Node.variable
verwenden müssen, um auf eines dieser Elemente in einer Member-Funktion zuzugreifen. Durch den Zugriff auf variable
wird versucht, auf eine lokale Variable namens variable
zuzugreifen.
self.element innerhalb des Konstruktors ist eine Instanzvariable (wenn ein Knotenobjekt seinen Wert ändert, ändert es sich nur für dieses Objekt), wobei das in der zweiten Version eine Klassenvariable ist (wenn also ein Knotenobjekt seinen Wert ändert, wird es für alle geändert.) Knotenobjekte).
Die Analogie in C++ wäre nicht statisch gegenüber statischen Elementvariablen in Ihrer Klasse.
Der wichtige Teil ist das Argument self
für __init__
. In der any instance-Methode wird dies das erste Argument sein. Dies geschieht durch das Design. In Python können Sie tatsächlich nur während des Methodenaufrufs auf die Instanz zugreifen. Dies wird explizit mit dem Argument self
angezeigt.
Wenn Sie sich in einer class
-Definition befinden, haben Sie noch keine Instanzen. Sie ändern also wirklich die Klasse selbst. Wenn Sie also Attribute auf Klassenebene definieren, werden sie zu Klassenattributen und nicht zu Instanzen.
Wenn man es mit einem C (++) vergleicht, könnte man wahrscheinlich sagen, dass "Klassen" in diesen Sprachen im Wesentlichen Blaupausen für die Objekte sind, die sie repräsentieren. "Diese Objekte müssen die Attribute foo
und bar
und zusätzlich die folgenden Methoden haben." In Python sind Klassen jedoch selbst Objekte, und ihre Hauptstärke besteht darin, dass sie Kopien (Instanzen) von sich selbst erstellen können, die auch die Methoden der Klasse verwenden. Es ist also eher so, als hätten Sie foo
und bar
als Klassenattribute und zusätzlich die folgende Methode, mit der Sie Instanzen erstellen können.
Anstelle eines Bauplans ist dies eher eine Schritt-für-Schritt-Anleitung.
self.element in der init ist eine Instanzvariable. Sie können sie in einer anderen Memberfunktion abrufen/setzen, indem Sie self.element eingeben. Das in der Klasse deklarierte Element ist die Klassenvariable. Sie können es durch Eingabe von Node.element abrufen/festlegen.
wenn Sie versuchen, auf die Variable mit einer Klasse zuzugreifen, sehen Sie nur in
cls.__dict__
wenn Sie jedoch versuchen, auf die Variable mit der Instanz zuzugreifen, wird sie zuerst angezeigt
self.__dict__
wenn find dann zurückkommt oder wenn nicht finden kann, dann schaut es auch rein
cls.__dict__
hier ist Cls die Klasse
class Test:
temp_1=10
temp_2=20
def __init__(self):
self.test_1=10
self.test_2=20
@classmethod
def c_test(cls):
pass
def t_method(self):
pass
print Test.__dict__
print Test().__dict__
Ausgabe:
{'c_test': <classmethod object at 0x7fede8f35a60>, '__module__': '__main__', 't_method': <function t_method at 0x7fede8f336e0>, 'temp_1': 10, '__doc__': None, '__init__': <function __init__ at 0x7fede8f335f0>, 'temp_2': 20}
{'test_2': 20, 'test_1': 10}
Für Details Klasse Spezialattribut