Heim > Artikel > Backend-Entwicklung > So verwenden Sie self in Python
Bevor wir die Verwendung von self in Python vorstellen, stellen wir zunächst die Klassen und Instanzen in Python vor.
Wir wissen, dass die wichtigsten Konzepte der Objektorientierung Klassen und Instanzen sind. Klassen sind abstrakte Vorlagen, wie zum Beispiel abstrakte Dinge, die dargestellt werden können von einer Schülerklasse. Instanzen sind spezifische „Objekte“, die auf der Grundlage von Klassen erstellt werden. Jedes Objekt erbt dieselben Methoden von der Klasse, seine Daten können jedoch unterschiedlich sein.
1. Nehmen Sie die Klasse Student als Beispiel. In Python ist die Klasse wie folgt definiert:
class Student(object): pass
(Object) gibt an, von welcher Klasse die Klasse erbt.
2. Nachdem die Klasse definiert wurde, können Sie eine Instanz von Student über den Klassennamen + () erstellen:
student = Student()
3. Sie können beim Erstellen einer Instanz die Attribute, von denen wir glauben, dass sie gebunden werden müssen, zwangsweise ausfüllen. Hier verwenden wir die Methode __init__
, eine in Python integrierte Methode. Verknüpfen Sie beispielsweise in der Student-Klasse Attribute wie Name und Punktzahl damit: __init__
方法,例如在Student类时,把name、score等属性绑上去:
class Student(object): def __init__(self, name, score): self.name = name self.score = score
这里注意:(1)、__init__
方法的第一参数永远是self
,表示创建的类实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。(2)、有了__init__
方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__
方法匹配的参数,但self不需要传,Python解释器会自己把实例变量传进去:
>>>student = Student("Hugh", 99) >>>student.name "Hugh" >>>student.score 99
另外,这里self
就是指类本身,self.name
就是Student
类的属性变量,是Student
类所有。而name
是外部传来的参数,不是Student
类所自带的。故,self.name = name
的意思就是把外部传来的参数name
的值赋值给Student类自己的属性变量self.name
。
4、和普通数相比,在类中定义函数只有一点不同,就是第一参数永远是类的本身实例变量self
,并且调用时,不用传递该参数。除此之外,类的方法(函数)和普通函数没啥区别,你既可以用默认参数、可变参数或者关键字参数(*args是可变参数,args接收的是一个tuple,**kw是关键字参数,kw接收的是一个dict)。
5、既然Student类实例本身就拥有这些数据,那么要访问这些数据,就没必要从外面的函数去访问,而可以直接在Student类的内部定义访问数据的函数(方法),这样,就可以把”数据”封装起来。这些封装数据的函数是和Student类本身是关联起来的,称之为类的方法:
class Student(obiect): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print "%s: %s" % (self.name, self.score)
>>>student = Student("Hugh", 99) >>>student.print_score Hugh: 99
这样一来,我们从外部看Student类,就只需要知道,创建实例需要给出name和score。而如何打印,都是在Student类的内部定义的,这些数据和逻辑被封装起来了,调用很容易,但却不知道内部实现的细节。
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线,在Python中,实例的变量名如果以开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:
class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print "%s: %s" %(self.__name,self.__score)
改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name
和实例变量.__score
了:
>>> student = Student('Hugh', 99) >>> student.__name Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute '__name'
这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
但是如果外部代码要获取name和score怎么办?可以给Student类增加get_name和get_score这样的方法:
class Student(object): ... def get_name(self): return self.__name def get_score(self): return self.__score
如果又要允许外部代码修改score怎么办?可以给Student类增加set_score方法:
class Student(object): ... def set_score(self, score): self.__score = score
需要注意的是,在Python中,变量名类似__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__
、__score__
这样的变量名。
有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
封装的另一个好处是可以随时给Student类增加新的方法,比如:get_grade
:
class Student(object): ... def get_grade(self): if self.score >= 90: return 'A' elif self.score >= 60: return 'B' else: return 'C'
同样的,get_grade
>>> student.get_grade() 'A'Hinweis hier: (1),
__init__-Methode ist immer <code>self
, der die erstellte Klasse instance selbst darstellt. Daher innerhalb der __init__Mit der Methode code> können Sie verschiedene Eigenschaften an self binden, da self auf die erstellte Instanz selbst verweist. (2) Mit der Methode <code>__init__
können Sie beim Erstellen einer Instanz keine leeren Parameter übergeben. Sie müssen Parameter übergeben, die mit der Methode __init__
übereinstimmen. Dies ist jedoch nicht erforderlich übergeben, übergibt der Python-Interpreter die Instanzvariablen selbst: 🎜class Test: def ppr(self): print(self) print(self.__class__) t = Test() t.ppr() 执行结果: <__main__.Test object at 0x000000000284E080> <class '__main__.Test'>🎜 Darüber hinaus bezieht sich hier
self
auf die Klasse selbst und self.name
ist Student sind Eigentum der Klasse <code>Student
. Und name
ist ein externer Parameter, nicht einer, der mit der Klasse Student
geliefert wird. Daher bedeutet self.name = name
, den Wert des externen Parameters name
der eigenen Attributvariablen self.name
der Student-Klasse zuzuweisen. 🎜🎜4. Im Vergleich zu gewöhnlichen Zahlen besteht der einzige Unterschied zwischen der Definition von Funktionen in einer Klasse darin, dass der erste Parameter immer die eigene Instanzvariable der Klasse ist. code>, und übergeben Sie diesen Parameter beim Aufruf nicht. Darüber hinaus unterscheiden sich Klassenmethoden (Funktionen) nicht von gewöhnlichen Funktionen. Sie können Standardparameter, variable Parameter oder Schlüsselwortparameter verwenden (*args sind variable Parameter, args empfängt ein Tupel). >, **kw ist ein Schlüsselwortparameter und kw erhält ein Diktat). 🎜🎜5. Da die Student-Klasseninstanz selbst Eigentümer dieser Daten ist, ist es nicht erforderlich, über externe Funktionen darauf zuzugreifen. Stattdessen können Sie die Funktion für den Zugriff auf die Daten direkt definieren Innerhalb der Student-Klasse (Methode) können auf diese Weise die „Daten“ gekapselt werden. Diese Funktionen, die Daten kapseln, sind mit der Student-Klasse selbst verknüpft und werden als Klassenmethoden bezeichnet: 🎜class Test: def ppr(): print(self) t = Test() t.ppr()
class Test: def ppr(): print(__class__) Test.ppr() 运行结果: <class '__main__.Test'>🎜 Auf diese Weise müssen wir, wenn wir die Student-Klasse von außen betrachten, nur wissen, dass der Name und die Punktzahl angegeben werden müssen gegeben, um eine Instanz zu erstellen. Wie gedruckt wird, wird innerhalb der Student-Klasse definiert. Diese Daten und Logik sind gekapselt, was den Aufruf erleichtert, aber Sie kennen die Details der internen Implementierung nicht. 🎜🎜Wenn Sie verhindern möchten, dass von außen auf interne Attribute zugegriffen wird, können Sie in Python zwei Unterstriche vor dem Namen des Attributs hinzufügen, wenn der Variablenname einer Instanz mit beginnt. Es wird zu einer privaten Variablen (privat), auf die nur intern, aber nicht von außen zugegriffen werden kann. Daher ändern wir die Student-Klasse: 🎜
class Parent: def pprt(self): print(self) class Child(Parent): def cprt(self): print(self) c = Child() c.cprt() c.pprt() p = Parent() p.pprt()🎜Nach der Änderung gibt es keine Änderung für den externen Code, aber auf die Instanz kann von außen nicht zugegriffen werden. Die Variable
.__name
und die Instanzvariable .__score
lauten: 🎜class Desc: def __get__(self, ins, cls): print('self in Desc: %s ' % self ) print(self, ins, cls) class Test: x = Desc() def prt(self): print('self in Test: %s' % self) t = Test() t.prt() t.x🎜Dadurch wird sichergestellt, dass externer Code den internen Zustand des Objekts nicht nach Belieben ändern kann, sodass der Schutz gewährleistet ist Aufgrund von Zugriffsbeschränkungen ist der Code robuster. 🎜🎜Aber was ist, wenn der externe Code den Namen und die Punktzahl erhalten möchte? Sie können der Student-Klasse Methoden wie get_name und get_score hinzufügen: 🎜
self in Test: <__main__.Test object at 0x00000000022570B8> self in Desc: <__main__.Desc object at 0x000000000223E208> <__main__.Desc object at 0x000000000223E208> None <class '__main__.Test'>🎜Was ist, wenn Sie zulassen möchten, dass externer Code die Punktzahl ändert? Sie können die set_score-Methode zur Student-Klasse hinzufügen: 🎜rrreee🎜Es ist zu beachten, dass Variablennamen in Python
__xxx__
ähneln, d. h. solche, die mit einem doppelten Unterstrich beginnen und mit einem Doppel enden Unterstriche sind spezielle Variablen. Auf spezielle Variablen kann direkt zugegriffen werden, nicht auf private Variablen. Daher können Variablennamen wie __name__
und __score__
nicht verwendet werden. 🎜🎜Manchmal werden Instanzvariablennamen angezeigt, die mit einem Unterstrich beginnen, z. B. _name. Auf solche Instanzvariablen kann jedoch gemäß der Konvention zugegriffen werden: „Obwohl auf mich zugegriffen werden kann.“ Bitte behandeln Sie mich als private Variable und greifen Sie nicht nach Belieben darauf zu. 🎜🎜Ein weiterer Vorteil der Kapselung besteht darin, dass Sie jederzeit neue Methoden zur Student-Klasse hinzufügen können, wie zum Beispiel: get_grade
:🎜rrreee🎜Ähnlich get_grade Methoden können direkt für Instanzvariablen aufgerufen werden, ohne die internen Implementierungsdetails zu kennen: 🎜<pre class="brush:py;">>>> student.get_grade()
&#39;A&#39;</pre><p>6、<code>self
的仔细用法class Test: def ppr(self): print(self) print(self.__class__) t = Test() t.ppr() 执行结果: <__main__.Test object at 0x000000000284E080> <class '__main__.Test'>
从上面的例子中可以很明显的看出,self代表的是类的实例。而self.__class__
则指向类。
注意:把self换成this,结果也一样,但Python中最好用约定俗成的self。
(2)、self可以不写吗?
在Python解释器的内部,当我们调用t.ppr()时,实际上Python解释成Test.ppr(t),也就是把self替换成了类的实例。
class Test: def ppr(): print(self) t = Test() t.ppr()
运行结果如下:
Traceback (most recent call last):
File "cl.py", line 6, in 4225fa317875f3e92281a7b1a5733569
t.ppr()
TypeError: ppr() takes 0 positional arguments but 1 was given
运行时提醒错误如下:ppr在定义时没有参数,但是我们运行时强行传了一个参数。
由于上面解释过了t.ppr()等同于Test.ppr(t),所以程序提醒我们多传了一个参数t。
这里实际上已经部分说明了self
在定义时不可以省略。
当然,如果我们的定义和调用时均不传类实例是可以的,这就是类方法。
class Test: def ppr(): print(__class__) Test.ppr() 运行结果: <class '__main__.Test'>
(3)、在继承时,传入的是哪个实例,就是那个传入的实例,而不是指定义了self的类的实例。
class Parent: def pprt(self): print(self) class Child(Parent): def cprt(self): print(self) c = Child() c.cprt() c.pprt() p = Parent() p.pprt()
运行结果:
3ba939ae2ab45e6446dfb7d3ccf3666f
3ba939ae2ab45e6446dfb7d3ccf3666f
37393a707a29b4383bd0bff8ef420bd6
解释:
运行c.cprt()时应该没有理解问题,指的是Child类的实例。
但是在运行c.pprt()时,等同于Child.pprt(c),所以self指的依然是Child类的实例,由于self中没有定义pprt()方法,所以沿着继承树往上找,发现在父类Parent中定义了pprt()方法,所以就会成功调用。
(4)、在描述符类中,self指的是描述符类的实例
class Desc: def __get__(self, ins, cls): print('self in Desc: %s ' % self ) print(self, ins, cls) class Test: x = Desc() def prt(self): print('self in Test: %s' % self) t = Test() t.prt() t.x
运行结果如下:
self in Test: 3072936af736f4c364572c56069bcf31
self in Desc: 12be4e5ac2680d4089b4937aa96557a0
12be4e5ac2680d4089b4937aa96557a0 3072936af736f4c364572c56069bcf31 1c85a171de3a3e74098b384eca9a5162
这里主要的疑问应该在:Desc类中定义的self不是应该是调用它的实例t吗?怎么变成了Desc类的实例了呢?
因为这里调用的是t.x,也就是说是Test类的实例t的属性x,由于实例t中并没有定义属性x,所以找到了类属性x,而该属性是描述符属性,为Desc类的实例而已,所以此处并没有顶用Test的任何方法。
那么我们如果直接通过类来调用属性x也可以得到相同的结果。
下面是把t.x改为Test.x运行的结果。
self in Test: <__main__.Test object at 0x00000000022570B8> self in Desc: <__main__.Desc object at 0x000000000223E208> <__main__.Desc object at 0x000000000223E208> None <class '__main__.Test'>
Das obige ist der detaillierte Inhalt vonSo verwenden Sie self in Python. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!