Python中self用法實(shí)例詳解
在介紹Python的self用法之前,先來(lái)介紹下Python中的類和實(shí)例……
我們知道,面向?qū)ο笞钪匾母拍罹褪穷悾╟lass)和實(shí)例(instance),類是抽象的模板,比如學(xué)生這個(gè)抽象的事物,可以用一個(gè)Student類來(lái)表示。而實(shí)例是根據(jù)類創(chuàng)建出來(lái)的一個(gè)個(gè)具體的“對(duì)象”,每一個(gè)對(duì)象都從類中繼承有相同的方法,但各自的數(shù)據(jù)可能不同。
1、以Student類為例,在Python中,定義類如下:
class Student(object): pass
(Object)表示該類從哪個(gè)類繼承下來(lái)的,Object類是所有類都會(huì)繼承的類。
2、實(shí)例:定義好了類,就可以通過(guò)Student類創(chuàng)建出Student的實(shí)例,創(chuàng)建實(shí)例是通過(guò)類名+()實(shí)現(xiàn):
student = Student()
3、由于類起到模板的作用,因此,可以在創(chuàng)建實(shí)例的時(shí)候,把我們認(rèn)為必須綁定的屬性強(qiáng)制填寫進(jìn)去。這里就用到Python當(dāng)中的一個(gè)內(nèi)置方法__init__
方法,例如在Student類時(shí),把name、score等屬性綁上去:
class Student(object): def __init__(self, name, score): self.name = name self.score = score
這里注意:(1)、__init__
方法的第一參數(shù)永遠(yuǎn)是self
,表示創(chuàng)建的類實(shí)例本身,因此,在__init__
方法內(nèi)部,就可以把各種屬性綁定到self,因?yàn)閟elf就指向創(chuàng)建的實(shí)例本身。(2)、有了__init__
方法,在創(chuàng)建實(shí)例的時(shí)候,就不能傳入空的參數(shù)了,必須傳入與__init__
方法匹配的參數(shù),但self不需要傳,Python解釋器會(huì)自己把實(shí)例變量傳進(jìn)去:
>>>student = Student("Hugh", 99) >>>student.name "Hugh" >>>student.score 99
另外,這里self
就是指類本身,self.name
就是Student
類的屬性變量,是Student
類所有。而name
是外部傳來(lái)的參數(shù),不是Student
類所自帶的。故,self.name = name
的意思就是把外部傳來(lái)的參數(shù)name
的值賦值給Student類自己的屬性變量self.name
。
4、和普通數(shù)相比,在類中定義函數(shù)只有一點(diǎn)不同,就是第一參數(shù)永遠(yuǎn)是類的本身實(shí)例變量self
,并且調(diào)用時(shí),不用傳遞該參數(shù)。除此之外,類的方法(函數(shù))和普通函數(shù)沒(méi)啥區(qū)別,你既可以用默認(rèn)參數(shù)、可變參數(shù)或者關(guān)鍵字參數(shù)(*args是可變參數(shù),args接收的是一個(gè)tuple,**kw是關(guān)鍵字參數(shù),kw接收的是一個(gè)dict)。
5、既然Student類實(shí)例本身就擁有這些數(shù)據(jù),那么要訪問(wèn)這些數(shù)據(jù),就沒(méi)必要從外面的函數(shù)去訪問(wèn),而可以直接在Student類的內(nèi)部定義訪問(wèn)數(shù)據(jù)的函數(shù)(方法),這樣,就可以把”數(shù)據(jù)”封裝起來(lái)。這些封裝數(shù)據(jù)的函數(shù)是和Student類本身是關(guān)聯(lián)起來(lái)的,稱之為類的方法:
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
這樣一來(lái),我們從外部看Student類,就只需要知道,創(chuàng)建實(shí)例需要給出name和score。而如何打印,都是在Student類的內(nèi)部定義的,這些數(shù)據(jù)和邏輯被封裝起來(lái)了,調(diào)用很容易,但卻不知道內(nèi)部實(shí)現(xiàn)的細(xì)節(jié)。
如果要讓內(nèi)部屬性不被外部訪問(wèn),可以把屬性的名稱前加上兩個(gè)下劃線,在Python中,實(shí)例的變量名如果以開頭,就變成了一個(gè)私有變量(private),只有內(nèi)部可以訪問(wèn),外部不能訪問(wèn),所以,我們把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)
改完后,對(duì)于外部代碼來(lái)說(shuō),沒(méi)什么變動(dòng),但是已經(jīng)無(wú)法從外部訪問(wèn)實(shí)例變量.__name
和實(shí)例變量.__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'
這樣就確保了外部代碼不能隨意修改對(duì)象內(nèi)部的狀態(tài),這樣通過(guò)訪問(wèn)限制的保護(hù),代碼更加健壯。
但是如果外部代碼要獲取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__
的,也就是以雙下劃線開頭,并且以雙下劃線結(jié)尾的,是特殊變量,特殊變量是可以直接訪問(wèn)的,不是private變量,所以,不能用__name__
、__score__
這樣的變量名。
有些時(shí)候,你會(huì)看到以一個(gè)下劃線開頭的實(shí)例變量名,比如_name,這樣的實(shí)例變量外部是可以訪問(wèn)的,但是,按照約定俗成的規(guī)定,當(dāng)你看到這樣的變量時(shí),意思就是,“雖然我可以被訪問(wèn),但是,請(qǐng)把我視為私有變量,不要隨意訪問(wèn)”。
封裝的另一個(gè)好處是可以隨時(shí)給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
方法可以直接在實(shí)例變量上調(diào)用,不需要知道內(nèi)部實(shí)現(xiàn)細(xì)節(jié):
>>> student.get_grade() 'A'
6、self
的仔細(xì)用法
(1)、self代表類的實(shí)例,而非類。
class Test: def ppr(self): print(self) print(self.__class__) t = Test() t.ppr() 執(zhí)行結(jié)果: <__main__.Test object at 0x000000000284E080> <class '__main__.Test'>
從上面的例子中可以很明顯的看出,self代表的是類的實(shí)例。而self.__class__
則指向類。
注意:把self換成this,結(jié)果也一樣,但Python中最好用約定俗成的self。
(2)、self可以不寫嗎?
在Python解釋器的內(nèi)部,當(dāng)我們調(diào)用t.ppr()時(shí),實(shí)際上Python解釋成Test.ppr(t),也就是把self替換成了類的實(shí)例。
class Test: def ppr(): print(self) t = Test() t.ppr()
運(yùn)行結(jié)果如下:
Traceback (most recent call last):
File "cl.py", line 6, in <module>
t.ppr()
TypeError: ppr() takes 0 positional arguments but 1 was given
運(yùn)行時(shí)提醒錯(cuò)誤如下:ppr在定義時(shí)沒(méi)有參數(shù),但是我們運(yùn)行時(shí)強(qiáng)行傳了一個(gè)參數(shù)。
由于上面解釋過(guò)了t.ppr()等同于Test.ppr(t),所以程序提醒我們多傳了一個(gè)參數(shù)t。
這里實(shí)際上已經(jīng)部分說(shuō)明了self
在定義時(shí)不可以省略。
當(dāng)然,如果我們的定義和調(diào)用時(shí)均不傳類實(shí)例是可以的,這就是類方法。
class Test: def ppr(): print(__class__) Test.ppr() 運(yùn)行結(jié)果: <class '__main__.Test'>
(3)、在繼承時(shí),傳入的是哪個(gè)實(shí)例,就是那個(gè)傳入的實(shí)例,而不是指定義了self的類的實(shí)例。
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()
運(yùn)行結(jié)果:
<__main__.Child object at 0x0000000002A47080>
<__main__.Child object at 0x0000000002A47080>
<__main__.Parent object at 0x0000000002A47240>
解釋:
運(yùn)行c.cprt()時(shí)應(yīng)該沒(méi)有理解問(wèn)題,指的是Child類的實(shí)例。
但是在運(yùn)行c.pprt()時(shí),等同于Child.pprt(c),所以self指的依然是Child類的實(shí)例,由于self中沒(méi)有定義pprt()方法,所以沿著繼承樹往上找,發(fā)現(xiàn)在父類Parent中定義了pprt()方法,所以就會(huì)成功調(diào)用。
(4)、在描述符類中,self指的是描述符類的實(shí)例
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
運(yùn)行結(jié)果如下:
self in Test: <__main__.Test object at 0x0000000002A570B8>
self in Desc: <__main__.Desc object at 0x000000000283E208>
<__main__.Desc object at 0x000000000283E208> <__main__.Test object at 0x0000000002A570B8> <class '__main__.Test'>
這里主要的疑問(wèn)應(yīng)該在:Desc類中定義的self不是應(yīng)該是調(diào)用它的實(shí)例t嗎?怎么變成了Desc類的實(shí)例了呢?
因?yàn)檫@里調(diào)用的是t.x,也就是說(shuō)是Test類的實(shí)例t的屬性x,由于實(shí)例t中并沒(méi)有定義屬性x,所以找到了類屬性x,而該屬性是描述符屬性,為Desc類的實(shí)例而已,所以此處并沒(méi)有頂用Test的任何方法。
那么我們?nèi)绻苯油ㄟ^(guò)類來(lái)調(diào)用屬性x也可以得到相同的結(jié)果。
下面是把t.x改為Test.x運(yùn)行的結(jié)果。
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'>
總結(jié):以上是之前學(xué)習(xí)Python時(shí)的小結(jié),現(xiàn)在已博客方式呈現(xiàn),同時(shí)為pyspark中調(diào)用self遇到的問(wèn)題做鋪墊,后面也會(huì)對(duì)比java,未完待續(xù)…….
到此這篇關(guān)于Python中self用法詳解的文章就介紹到這了,更多相關(guān)Python中self用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實(shí)現(xiàn)旋轉(zhuǎn)和水平翻轉(zhuǎn)的方法
今天小編就為大家分享一篇python實(shí)現(xiàn)旋轉(zhuǎn)和水平翻轉(zhuǎn)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-10-10正則化DropPath/drop_path用法示例(Python實(shí)現(xiàn))
DropPath 類似于Dropout,不同的是 Drop將深度學(xué)習(xí)模型中的多分支結(jié)構(gòu)隨機(jī)"失效",而Dropout是對(duì)神經(jīng)元隨機(jī)"失效"這篇文章主要給大家介紹了關(guān)于正則化DropPath/drop_path用法的相關(guān)資料,需要的朋友可以參考下2022-04-04Pytorch中求模型準(zhǔn)確率的兩種方法小結(jié)
這篇文章主要介紹了Pytorch中求模型準(zhǔn)確率的兩種方法小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-05-05使用IPython下的Net-SNMP來(lái)管理類UNIX系統(tǒng)的教程
這篇文章主要介紹了使用IPython下的Net-SNMP來(lái)管理類UNIX系統(tǒng)的教程,本文來(lái)自于IBM官方網(wǎng)站技術(shù)文檔,需要的朋友可以參考下2015-04-04Python 套接字 Accept 超時(shí)問(wèn)題解析
本文討論了 Python 中套接字的超時(shí)功能,該功能對(duì)于緩解無(wú)限期等待套接字接受的問(wèn)題是必要的,下面通過(guò)本文給大家大家介紹Python 套接字 Accept 超時(shí)問(wèn)題,需要的朋友可以參考下2023-06-06Python實(shí)現(xiàn)遍歷目錄的方法【測(cè)試可用】
這篇文章主要介紹了Python實(shí)現(xiàn)遍歷目錄的方法,涉及Python針對(duì)目錄與文件的遍歷、判斷、讀取相關(guān)操作技巧,需要的朋友可以參考下2017-03-032020新版本pycharm+anaconda+opencv+pyqt環(huán)境配置學(xué)習(xí)筆記,親測(cè)可用
這篇文章主要介紹了2020新版本pycharm+anaconda+opencv+pyqt環(huán)境配置學(xué)習(xí)筆記,親測(cè)可用,特此分享到腳本之家平臺(tái),需要的朋友可以參考下2020-03-03淺談Python中的可變對(duì)象和不可變對(duì)象
下面小編就為大家?guī)?lái)一篇淺談Python中的可變對(duì)象和不可變對(duì)象。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07