Wenn Sie schon seit einiger Zeit in Python (objektorientierte Programmierung) programmieren, sind Sie definitiv auf Methoden gestoßen, self
deren erster Parameter dies ist.
Versuchen wir zunächst zu verstehen, was dieser wiederkehrende Selbstparameter ist.
Was ist Selbst in Python?
Bei der objektorientierten Programmierung verwenden wir immer dann, wenn wir Methoden für eine Klasse definieren, self
als ersten Parameter. Schauen wir uns die Definition einer Klasse mit dem Namen an Cat
.
class Cat: def __init__(self, name, age): self.name = name self.age = age def info(self): print(f"I am a cat. My name is (self.name). I am (self.age) years old.") def make_sound(self): print("Meow")
In diesem Fall haben alle Methoden, einschließlich __init__
, den ersten Parameter als self
.
Wir wissen, dass Klasse eine Blaupause für die Objekte ist. Mit dieser Blaupause können mehrere Objekte erstellt werden. Lassen Sie uns zwei verschiedene Objekte aus der obigen Klasse erstellen.
cat1 = Cat('Andy', 2) cat2 = Cat('Phoebe', 3)
Das self
Schlüsselwort wird verwendet, um eine Instanz (ein Objekt) der angegebenen Klasse darzustellen. In diesem Fall sind die beiden Cat
Objekte cat1
und cat2
haben ihre eigenen name
und age
Attribute. Wenn es kein Selbstargument gab, konnte dieselbe Klasse die Informationen für diese beiden Objekte nicht enthalten.
Da die Klasse jedoch nur eine Blaupause ist, self
können Sie auf die Attribute und Methoden jedes Objekts in Python zugreifen. Dadurch kann jedes Objekt seine eigenen Attribute und Methoden haben. Daher verweisen wir bereits lange vor dem Erstellen dieser Objekte auf die Objekte, self
während wir die Klasse definieren.
Warum wird das Selbst jedes Mal explizit definiert?
Selbst wenn wir die Verwendung von verstehen self
, mag es seltsam erscheinen, insbesondere für Programmierer aus anderen Sprachen, die self
jedes Mal, wenn wir eine Methode definieren, explizit als Parameter übergeben werden. Wie das Zen von Python sagt: " Explizit ist besser als implizit ".
Warum müssen wir das tun? Nehmen wir zunächst ein einfaches Beispiel. Wir haben eine Point
Klasse, die eine Methode distance
zur Berechnung der Entfernung vom Ursprung definiert.
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
Lassen Sie uns nun diese Klasse instanziieren und die Entfernung finden.
>>> p1 = Point(6,8) >>> p1.distance() 10.0
Im obigen Beispiel werden __init__()
drei Parameter definiert, aber wir haben gerade zwei (6 und 8) übergeben. Ebenso distance()
erfordert eins, aber null Argumente wurden übergeben. Warum beschwert sich Python nicht über diese Nichtübereinstimmung der Argumentnummer?
Was passiert intern?
Point.distance
und p1.distance
im obigen Beispiel sind unterschiedlich und nicht genau gleich.
>>> type(Point.distance) >>> type(p1.distance)
Wir können sehen, dass die erste eine Funktion und die zweite eine Methode ist. Eine Besonderheit bei Methoden (in Python) ist, dass das Objekt selbst als erstes Argument an die entsprechende Funktion übergeben wird.
Im obigen Beispiel entspricht der Methodenaufruf p1.distance()
tatsächlich Point.distance(p1)
.
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. So etwas wie obj.meth(args)
wird 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 self
ist lediglich eine Konvention. Es ist kein Schlüsselwort und hat in Python keine besondere Bedeutung. Wir könnten andere Namen (wie this
) verwenden, aber es wird dringend davon abgeraten. Die Verwendung anderer Namen als die self
, die von den meisten Entwicklern missbilligt werden, beeinträchtigt die Lesbarkeit des Codes ( Lesbarkeit zählt ).
Selbst kann vermieden werden
Inzwischen ist klar, dass das Objekt (die Instanz) selbst automatisch als erstes Argument weitergegeben wird. Dieses implizite Verhalten kann bei der Erstellung einer statischen Methode vermieden werden. Betrachten Sie das folgende einfache Beispiel:
class A(object): @staticmethod def stat_meth(): print("Look no self was passed")
Hier @staticmethod
ist ein Funktionsdekorateur, der stat_meth()
statisch macht . Lassen Sie uns diese Klasse instanziieren und die Methode aufrufen.
>>> a = A() >>> a.stat_meth() Look no self was passed
Aus dem obigen Beispiel können wir erkennen, dass das implizite Verhalten der Übergabe des Objekts als erstes Argument bei Verwendung einer statischen Methode vermieden wurde. Alles in allem verhalten sich statische Methoden wie die einfachen alten Funktionen (da alle Objekte einer Klasse statische Methoden gemeinsam haben).
>>> type(A.stat_meth) >>> type(a.stat_meth)
Selbst ist hier, um zu bleiben
Das Explizite self
gilt nicht nur für Python. Diese Idee wurde von Modula-3 entlehnt . Das Folgende ist ein Anwendungsfall, in dem es hilfreich wird.
In Python gibt es keine explizite Variablendeklaration. Sie treten bei der ersten Aufgabe in Aktion. Die Verwendung von self
erleichtert die Unterscheidung zwischen Instanzattributen (und Methoden) und lokalen Variablen.
Im ersten Beispiel ist self.x ein Instanzattribut, während x eine lokale Variable ist. Sie sind nicht gleich und liegen in verschiedenen Namespaces.
Viele haben vorgeschlagen, sich in Python zu einem Schlüsselwort zu machen, wie this
in C ++ und Java. Dies würde die redundante Verwendung von explizit self
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.
__init __ () ist kein Konstruktor
Eine wichtige Schlussfolgerung, die aus den bisherigen Informationen gezogen werden kann, ist, dass die __init__()
Methode kein Konstruktor ist. Viele naive Python-Programmierer werden damit verwechselt, da __init__()
sie beim Erstellen eines Objekts aufgerufen werden.
A closer inspection will reveal that the first parameter in __init__()
is the object itself (object already exists). The function __init__()
is called immediately after the object is created and is used to initialize it.
Technically speaking, a constructor is a method which creates the object itself. In Python, this method is __new__()
. A common signature of this method is:
__new__(cls, *args, **kwargs)
When __new__()
is called, the class itself is passed as the first argument automatically(cls
).
Again, like self, cls is just a naming convention. Furthermore, *args and **kwargs are used to take an arbitrary number of arguments during method calls in Python.
Some important things to remember when implementing __new__()
are:
__new__()
is always called before__init__()
.- First argument is the class itself which is passed implicitly.
- Always return a valid object from
__new__()
. Not mandatory, but its main use is to create and return an object.
Let's take a look at an example:
class Point(object): def __new__(cls,*args,**kwargs): print("From new") print(cls) print(args) print(kwargs) # create our object and return it obj = super().__new__(cls) return obj def __init__(self,x = 0,y = 0): print("From init") self.x = x self.y = y
Now, let's now instantiate it.
>>> p2 = Point(3,4) From new (3, 4) () From init
This example illustrates that __new__()
is called before __init__()
. We can also see that the parameter cls in __new__()
is the class itself (Point
). Finally, the object is created by calling the __new__()
method on object base class.
In Python, object
is the base class from which all other classes are derived. In the above example, we have done this using super().
Use __new__ or __init__?
You might have seen __init__()
very often but the use of __new__()
is rare. This is because most of the time you don't need to override it. Generally, __init__()
is used to initialize a newly created object while __new__()
is used to control the way an object is created.
We can also use __new__()
to initialize attributes of an object, but logically it should be inside __init__()
.
One practical use of __new__()
, however, could be to restrict the number of objects created from a class.
Suppose we wanted a class SqPoint
for creating instances to represent the four vertices of a square. We can inherit from our previous class Point
(the second example in this article) and use __new__()
to implement this restriction. Here is an example to restrict a class to have only four instances.
class SqPoint(Point): MAX_Inst = 4 Inst_created = 0 def __new__(cls,*args,**kwargs): if (cls.Inst_created>= cls.MAX_Inst): raise ValueError("Cannot create more objects") cls.Inst_created += 1 return super().__new__(cls)
Ein Probelauf:
>>> p1 = SqPoint(0,0) >>> p2 = SqPoint(1,0) >>> p3 = SqPoint(1,1) >>> p4 = SqPoint(0,1) >>> >>> p5 = SqPoint(2,2) Traceback (most recent call last):… ValueError: Cannot create more objects