python---之物件導向self
Python中self用法詳解
在介紹Python的self用法之前,先來介紹下Python中的類和例項……
我們知道,物件導向最重要的概念就是類(class)和例項(instance),類是抽象的模板,比如學生這個抽象的事物,可以用一個Student類來表示。而例項是根據類建立出來的一個個具體的“物件”,每一個物件都從類中繼承有相同的方法,但各自的資料可能不同。
1、以Student類為例,在Python中,定義類如下:
class Student(object):
pass
- 1
- 2
(Object)表示該類從哪個類繼承下來的,Object類是所有類都會繼承的類。
2、例項:定義好了類,就可以通過Student類建立出Student的例項,建立例項是通過類名+()實現:
student = Student()
- 1
3、由於類起到模板的作用,因此,可以在建立例項的時候,把我們認為必須繫結的屬性強制填寫進去。這裡就用到Python當中的一個內建方法__init__
方法,例如在Student類時,把name、score等屬性綁上去:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
- 1
- 2
- 3
- 4
這裡注意:(1)、__init__
方法的第一引數永遠是self
,表示建立的類例項本身,因此,在__init__
方法內部,就可以把各種屬性繫結到self,因為self就指向建立的例項本身。(2)、有了__init__
方法,在建立例項的時候,就不能傳入空的引數了,必須傳入與__init__
方法匹配的引數,但self不需要傳,Python直譯器會自己把例項變數傳進去:
>>>student = Student("Hugh", 99)
>>>student.name
"Hugh"
>>>student.score
99
- 1
- 2
- 3
- 4
- 5
另外,這裡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)
- 1
- 2
- 3
- 4
- 5
- 6
>>>student = Student("Hugh", 99)
>>>student.print_score
Hugh: 99
- 1
- 2
- 3
這樣一來,我們從外部看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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
改完後,對於外部程式碼來說,沒什麼變動,但是已經無法從外部訪問例項變數.__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'
- 1
- 2
- 3
- 4
- 5
這樣就確保了外部程式碼不能隨意修改物件內部的狀態,這樣通過訪問限制的保護,程式碼更加健壯。
但是如果外部程式碼要獲取name和score怎麼辦?可以給Student類增加get_name和get_score這樣的方法:
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
如果又要允許外部程式碼修改score怎麼辦?可以給Student類增加set_score方法:
class Student(object):
...
def set_score(self, score):
self.__score = score
- 1
- 2
- 3
- 4
- 5
需要注意的是,在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'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
同樣的,get_grade
方法可以直接在例項變數上呼叫,不需要知道內部實現細節:
>>> student.get_grade()
'A'
- 1
- 2
6、self
的仔細用法
(1)、self代表類的例項,而非類。
class Test:
def ppr(self):
print(self)
print(self.__class__)
t = Test()
t.ppr()
執行結果:
<__main__.Test object at 0x000000000284E080>
<class '__main__.Test'>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
從上面的例子中可以很明顯的看出,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()
- 1
- 2
- 3
- 4
- 5
- 6
執行結果如下:
Traceback (most recent call last):
File "cl.py", line 6, in <module>
t.ppr()
TypeError: ppr() takes 0 positional arguments but 1 was given
- 1
- 2
- 3
- 4
執行時提醒錯誤如下:ppr在定義時沒有引數,但是我們執行時強行傳了一個引數。
由於上面解釋過了t.ppr()等同於Test.ppr(t),所以程式提醒我們多傳了一個引數t。
這裡實際上已經部分說明了self
在定義時不可以省略。
當然,如果我們的定義和呼叫時均不傳類例項是可以的,這就是類方法。
class Test:
def ppr():
print(__class__)
Test.ppr()
執行結果:
<class '__main__.Test'>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
(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()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
執行結果:
<__main__.Child object at 0x0000000002A47080>
<__main__.Child object at 0x0000000002A47080>
<__main__.Parent object at 0x0000000002A47240>
- 1
- 2
- 3
解釋:
執行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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
執行結果如下:
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'>
- 1
- 2
- 3
這裡主要的疑問應該在: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'>
- 1
- 2
轉載:https://blog.csdn.net/CLHugh/article/details/75000104
- 3
總結:以上是之前學習Python時的小結,現在已部落格方式呈現,同時為pyspark中呼叫self遇到的問題做鋪墊,後面也會對比java,未完待續…….
相關文章
- python---之cls,和selfPython
- python---核心知識12之物件導向三大特性補充Python物件
- php物件導向知識 this,self,parent的區別PHP物件
- 物件導向-物件導向思想物件
- 【讀】JavaScript之物件導向JavaScript物件
- 物件導向之繼承物件繼承
- PHP物件導向之&引用PHP物件
- 二、Java之物件導向Java物件
- 《JavaScript物件導向精要》之六:物件模式JavaScript物件模式
- python之成員(物件導向)Python物件
- Go 筆記之物件導向Go筆記物件
- 物件導向之內部類物件
- 物件導向之_繼承概念物件繼承
- Python進階之物件導向Python物件
- Golang 基礎之物件導向Golang物件
- Python基礎之物件導向Python物件
- Python物件導向之九:反射Python物件反射
- 物件導向之封裝(Java)物件封裝Java
- Python之物件導向和麵向過程Python物件
- 物件導向與程式導向物件
- 程式導向與物件導向物件
- “程序導向”和“物件導向”物件
- 『無為則無心』Python物件導向 — 47、Python中的self詳解Python物件
- 物件導向之 開閉原則物件
- PHP基礎之物件導向篇PHP物件
- 21. 物件導向之繼承物件繼承
- 20. 物件導向之封裝物件封裝
- 【Kotlin】初識Kotlin之物件導向Kotlin物件
- Java正式上路之物件導向一Java物件
- Java正式上路之物件導向二Java物件
- 物件導向物件
- 物件導向,搞定物件物件
- JAVA物件導向基礎--物件導向介紹Java物件
- PHP 物件導向 (九)物件導向三大特徵PHP物件特徵
- javaSE之物件導向思維導圖總結Java物件
- 【物件導向的PHP】之模式:目錄物件PHP模式
- 物件導向 -- 三大特性之封裝物件封裝
- 《JavaScript物件導向精要》之五:繼承JavaScript物件繼承