Okay, das Wichtigste zuerst.
In Python gibt es keine "Variablendeklaration" oder "Variableninitialisierung".
Es gibt einfach das, was wir "Zuweisung" nennen, aber wahrscheinlich nur "Benennung".
Zuweisung bedeutet "dieser Name auf der linken Seite bezieht sich jetzt auf das Ergebnis der Bewertung der rechten Seite, unabhängig davon, worauf zuvor Bezug genommen wurde (wenn überhaupt)".
foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication
Daher haben Pythons Namen (wahrscheinlich ein besserer Begriff als "Variablen") keine zugeordneten Typen. die Werte tun. Sie können denselben Namen unabhängig von seinem Typ erneut auf alles anwenden, aber das Ding hat immer noch ein Verhalten, das von seinem Typ abhängt. Der Name ist einfach eine Möglichkeit, sich auf den Wert (das Objekt) zu beziehen. Dies beantwortet Ihre zweite Frage: Sie erstellen keine Variablen für einen benutzerdefinierten Typ. Sie erstellen keine Variablen für einen bestimmten Typ. Sie "erstellen" überhaupt keine Variablen. Sie geben Objekten Namen.
Zweiter Punkt: Python folgt einer sehr einfachen Regel, wenn es um Klassen geht, die tatsächlich viel konsistenter ist als die Sprachen wie Java, C ++ und C #: Alles, was im class
Block deklariert ist , ist Teil der Klasse . def
Hier geschriebene Funktionen ( ) sind also Methoden, dh ein Teil des Klassenobjekts (nicht auf Instanzbasis gespeichert), genau wie in Java, C ++ und C #; aber auch andere Namen hier sind auch Teil der Klasse. Auch hier sind die Namen nur Namen, und sie haben keine zugeordneten Typen, und Funktionen sind auch Objekte in Python. So:
class Example:
data = 42
def method(self): pass
Klassen sind in Python auch Objekte .
Jetzt haben wir ein Objekt mit dem Namen erstellt Example
, das die Klasse aller Dinge darstellt, die Example
s sind. Dieses Objekt verfügt über zwei vom Benutzer bereitgestellte Attribute (in C ++ "Mitglieder"; in C # "Felder oder Eigenschaften oder Methoden"; in Java "Felder oder Methoden"). Einer von ihnen heißt data
und speichert den ganzzahligen Wert 42
. Der andere heißt method
und speichert ein Funktionsobjekt. (Es gibt mehrere weitere Attribute, die Python automatisch hinzufügt.)
Diese Attribute sind jedoch immer noch nicht wirklich Teil des Objekts. Grundsätzlich ist ein Objekt nur ein Bündel weiterer Namen (die Attributnamen), bis Sie zu Dingen kommen, die nicht mehr aufgeteilt werden können. Auf diese Weise können Werte zwischen verschiedenen Instanzen einer Klasse oder sogar zwischen Objekten verschiedener Klassen geteilt werden, wenn Sie dies absichtlich einrichten.
Erstellen wir eine Instanz:
x = Example()
Jetzt haben wir ein separates Objekt namens x
, das eine Instanz von ist Example
. Die data
und method
sind eigentlich nicht Teil des Objekts, aber wir können sie trotzdem nachschlagen, x
weil Python hinter den Kulissen etwas magisches tut. method
Insbesondere wenn wir nachschlagen , erhalten wir stattdessen eine "gebundene Methode" (wenn wir sie aufrufen, x
wird sie automatisch als self
Parameter übergeben, was nicht passieren kann, wenn wir Example.method
direkt nachschlagen ).
Was passiert, wenn wir versuchen zu verwenden x.data
?
Wenn wir es untersuchen, wird es zuerst im Objekt nachgeschlagen. Wenn es nicht im Objekt gefunden wird, sucht Python in der Klasse.
Wenn wir jedoch zuweisen x.data
, erstellt Python ein Attribut für das Objekt. Das Attribut der Klasse wird nicht ersetzt.
Dies ermöglicht uns die Objektinitialisierung . Python ruft die __init__
Methode der Klasse bei neuen Instanzen automatisch auf, wenn sie erstellt werden, falls vorhanden. Bei dieser Methode können wir einfach Attribute zuweisen, um Anfangswerte für dieses Attribut für jedes Objekt festzulegen:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
# rest as before
Jetzt müssen name
wir ein angeben, wenn wir ein erstellen Example
, und jede Instanz hat ihre eigene name
. Python ignoriert das Klassenattribut, Example.name
wenn wir das .name
einer Instanz nachschlagen , da das Attribut der Instanz zuerst gefunden wird.
Eine letzte Einschränkung: Modifikation (Mutation) und Zuordnung sind verschiedene Dinge!
In Python sind Zeichenfolgen unveränderlich. Sie können nicht geändert werden. Wenn Sie das tun:
a = 'hi '
b = a
a += 'mom'
Sie ändern die ursprüngliche 'Hi'-Zeichenfolge nicht. Das ist in Python unmöglich. Stattdessen erstellen Sie eine neue Zeichenfolge 'hi mom'
und veranlassen a
, kein Name mehr zu sein 'hi '
, und beginnen 'hi mom'
stattdessen , ein Name für zu sein. Wir haben auch b
einen Namen für erstellt 'hi '
und nach dem erneuten Anwenden des a
Namens b
ist er immer noch ein Name für 'hi '
, da er 'hi '
noch vorhanden ist und nicht geändert wurde.
Listen können jedoch geändert werden:
a = [1, 2, 3]
b = a
a += [4]
Jetzt b
ist auch [1, 2, 3, 4], weil wir b
einen Namen für dasselbe gemacht haben, das a
benannt wurde, und dann haben wir dieses Ding geändert. Wir haben keine neue Liste für a
den Namen erstellt, da Python +=
für Listen einfach anders behandelt .
Dies ist für Objekte von Bedeutung, da die Änderung in allen anderen Instanzen "gesehen" wird, wenn Sie eine Liste als Klassenattribut haben und eine Instanz zum Ändern der Liste verwenden. Dies liegt daran, dass (a) die Daten tatsächlich Teil des Klassenobjekts und kein Instanzobjekt sind; (b) Da Sie die Liste geändert und keine einfache Zuweisung vorgenommen haben, haben Sie kein neues Instanzattribut erstellt, das das Klassenattribut verbirgt.