Python中self用法實例詳解
在介紹Python的self用法之前,先來介紹下Python中的類和實例……
我們知道,面向?qū)ο笞钪匾母拍罹褪穷悾╟lass)和實例(instance),類是抽象的模板,比如學(xué)生這個抽象的事物,可以用一個Student類來表示。而實例是根據(jù)類創(chuàng)建出來的一個個具體的“對象”,每一個對象都從類中繼承有相同的方法,但各自的數(shù)據(jù)可能不同。
1、以Student類為例,在Python中,定義類如下:
class Student(object):
pass
(Object)表示該類從哪個類繼承下來的,Object類是所有類都會繼承的類。
2、實例:定義好了類,就可以通過Student類創(chuàng)建出Student的實例,創(chuàng)建實例是通過類名+()實現(xiàn):
student = Student()
3、由于類起到模板的作用,因此,可以在創(chuàng)建實例的時候,把我們認為必須綁定的屬性強制填寫進去。這里就用到Python當中的一個內(nèi)置方法__init__方法,例如在Student類時,把name、score等屬性綁上去:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score這里注意:(1)、__init__方法的第一參數(shù)永遠是self,表示創(chuàng)建的類實例本身,因此,在__init__方法內(nèi)部,就可以把各種屬性綁定到self,因為self就指向創(chuàng)建的實例本身。(2)、有了__init__方法,在創(chuàng)建實例的時候,就不能傳入空的參數(shù)了,必須傳入與__init__方法匹配的參數(shù),但self不需要傳,Python解釋器會自己把實例變量傳進去:
>>>student = Student("Hugh", 99)
>>>student.name
"Hugh"
>>>student.score
99
另外,這里self就是指類本身,self.name就是Student類的屬性變量,是Student類所有。而name是外部傳來的參數(shù),不是Student類所自帶的。故,self.name = name的意思就是把外部傳來的參數(shù)name的值賦值給Student類自己的屬性變量self.name。
4、和普通數(shù)相比,在類中定義函數(shù)只有一點不同,就是第一參數(shù)永遠是類的本身實例變量self,并且調(diào)用時,不用傳遞該參數(shù)。除此之外,類的方法(函數(shù))和普通函數(shù)沒啥區(qū)別,你既可以用默認參數(shù)、可變參數(shù)或者關(guān)鍵字參數(shù)(*args是可變參數(shù),args接收的是一個tuple,**kw是關(guān)鍵字參數(shù),kw接收的是一個dict)。
5、既然Student類實例本身就擁有這些數(shù)據(jù),那么要訪問這些數(shù)據(jù),就沒必要從外面的函數(shù)去訪問,而可以直接在Student類的內(nèi)部定義訪問數(shù)據(jù)的函數(shù)(方法),這樣,就可以把”數(shù)據(jù)”封裝起來。這些封裝數(shù)據(jù)的函數(shù)是和Student類本身是關(guān)聯(lián)起來的,稱之為類的方法:
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類,就只需要知道,創(chuàng)建實例需要給出name和score。而如何打印,都是在Student類的內(nèi)部定義的,這些數(shù)據(jù)和邏輯被封裝起來了,調(diào)用很容易,但卻不知道內(nèi)部實現(xiàn)的細節(jié)。
如果要讓內(nèi)部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線,在Python中,實例的變量名如果以開頭,就變成了一個私有變量(private),只有內(nèi)部可以訪問,外部不能訪問,所以,我們把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)
改完后,對于外部代碼來說,沒什么變動,但是已經(jīng)無法從外部訪問實例變量.__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'這樣就確保了外部代碼不能隨意修改對象內(nèi)部的狀態(tài),這樣通過訪問限制的保護,代碼更加健壯。
但是如果外部代碼要獲取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é)尾的,是特殊變量,特殊變量是可以直接訪問的,不是private變量,所以,不能用__name__、__score__這樣的變量名。
有些時候,你會看到以一個下劃線開頭的實例變量名,比如_name,這樣的實例變量外部是可以訪問的,但是,按照約定俗成的規(guī)定,當你看到這樣的變量時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變量,不要隨意訪問”。
封裝的另一個好處是可以隨時給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方法可以直接在實例變量上調(diào)用,不需要知道內(nèi)部實現(xiàn)細節(jié):
>>> student.get_grade() 'A'
6、self的仔細用法
(1)、self代表類的實例,而非類。
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代表的是類的實例。而self.__class__則指向類。
注意:把self換成this,結(jié)果也一樣,但Python中最好用約定俗成的self。
(2)、self可以不寫嗎?
在Python解釋器的內(nèi)部,當我們調(diào)用t.ppr()時,實際上Python解釋成Test.ppr(t),也就是把self替換成了類的實例。
class Test:
def ppr():
print(self)
t = Test()
t.ppr()運行結(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
運行時提醒錯誤如下:ppr在定義時沒有參數(shù),但是我們運行時強行傳了一個參數(shù)。
由于上面解釋過了t.ppr()等同于Test.ppr(t),所以程序提醒我們多傳了一個參數(shù)t。
這里實際上已經(jīng)部分說明了self在定義時不可以省略。
當然,如果我們的定義和調(diào)用時均不傳類實例是可以的,這就是類方法。
class Test:
def ppr():
print(__class__)
Test.ppr()
運行結(jié)果:
<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()運行結(jié)果:
<__main__.Child object at 0x0000000002A47080>
<__main__.Child object at 0x0000000002A47080>
<__main__.Parent object at 0x0000000002A47240>
解釋:
運行c.cprt()時應(yīng)該沒有理解問題,指的是Child類的實例。
但是在運行c.pprt()時,等同于Child.pprt(c),所以self指的依然是Child類的實例,由于self中沒有定義pprt()方法,所以沿著繼承樹往上找,發(fā)現(xiàn)在父類Parent中定義了pprt()方法,所以就會成功調(diào)用。
(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運行結(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'>
這里主要的疑問應(yīng)該在:Desc類中定義的self不是應(yīng)該是調(diào)用它的實例t嗎?怎么變成了Desc類的實例了呢?
因為這里調(diào)用的是t.x,也就是說是Test類的實例t的屬性x,由于實例t中并沒有定義屬性x,所以找到了類屬性x,而該屬性是描述符屬性,為Desc類的實例而已,所以此處并沒有頂用Test的任何方法。
那么我們?nèi)绻苯油ㄟ^類來調(diào)用屬性x也可以得到相同的結(jié)果。
下面是把t.x改為Test.x運行的結(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é)習Python時的小結(jié),現(xiàn)在已博客方式呈現(xiàn),同時為pyspark中調(diào)用self遇到的問題做鋪墊,后面也會對比java,未完待續(xù)…….
到此這篇關(guān)于Python中self用法詳解的文章就介紹到這了,更多相關(guān)Python中self用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談keras中l(wèi)oss與val_loss的關(guān)系
這篇文章主要介紹了淺談keras中l(wèi)oss與val_loss的關(guān)系,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06
Python3調(diào)用百度AI識別圖片中的文字功能示例【測試可用】
這篇文章主要介紹了Python3調(diào)用百度AI識別圖片中的文字功能,結(jié)合實例形式分析了Python3安裝及使用百度AI接口的相關(guān)操作技巧,并附帶說明了百度官方AI平臺的注冊及接口調(diào)用操作方法,需要的朋友可以參考下2019-03-03
Python使用try except處理程序異常的三種常用方法分析
這篇文章主要介紹了Python使用try except處理程序異常的三種常用方法,結(jié)合實例形式分析了Python基于try except語句針對異常的捕獲、查看、回溯等相關(guān)操作技巧,需要的朋友可以參考下2018-09-09
python用TensorFlow做圖像識別的實現(xiàn)
這篇文章主要介紹了python用TensorFlow做圖像識別的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2020-04-04
pytorch中LN(LayerNorm)及Relu和其變相的輸出操作
這篇文章主要介紹了pytorch中LN(LayerNorm)及Relu和其變相的輸出操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-05-05
python 腳本生成隨機 字母 + 數(shù)字密碼功能
本文通過一小段簡單的代碼給大家分享基于python 腳本生成隨機 字母 + 數(shù)字密碼功能,感興趣的朋友跟隨腳本之家小編一起學(xué)習吧2018-05-05
Python?OpenCV?Canny邊緣檢測算法的原理實現(xiàn)詳解
這篇文章主要介紹了Python?OpenCV?Canny邊緣檢測算法的原理實現(xiàn)詳解,由于邊緣檢測對噪聲敏感,因此對圖像應(yīng)用高斯平滑以幫助減少噪聲,具體詳情需要的小伙伴可以參考一下2022-07-07

