在介紹Python的self用法之前,先來介紹下Python中的類別和實例
我們知道,物件導向最重要的概念就是類別(class)和實例(instance),類別是抽象的模板,例如學生這個抽象的事物,可以用一個Student類別來表示。而實例是根據類別創建出來的一個個具體的“物件”,每個物件都從類別中繼承有相同的方法,但各自的資料可能不同。
1、以Student類別為例,在Python中,定義類別如下:
class Student(object): pass
(Object)表示該類別從哪個類別繼承下來的,Object類別是所有類別都會繼承的類別。
2、實例:定義好了類,就可以透過Student類別建立出Student的實例,建立實例是透過類別名稱()實作:
student = Student()
3、因為類別起到模板的作用,因此,可以在創建實例的時候,把我們認為必須綁定的屬性強制填寫進去。這裡就用到Python當中的一個內建方法__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'
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'>
从上面的例子中可以很明显的看出,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
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()
运行结果:
<__main__.child object at>
<__main__.child object at>
<__main__.parent object at>
解释:
运行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: <__main__.test object at>
self in Desc: <__main__.desc object at>
<__main__.desc object at> <__main__.test object at>
这里主要的疑问应该在: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'>
以上是Python中的self怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python在遊戲和GUI開發中表現出色。 1)遊戲開發使用Pygame,提供繪圖、音頻等功能,適合創建2D遊戲。 2)GUI開發可選擇Tkinter或PyQt,Tkinter簡單易用,PyQt功能豐富,適合專業開發。

Python适合数据科学、Web开发和自动化任务,而C 适用于系统编程、游戏开发和嵌入式系统。Python以简洁和强大的生态系统著称,C 则以高性能和底层控制能力闻名。

2小時內可以學會Python的基本編程概念和技能。 1.學習變量和數據類型,2.掌握控制流(條件語句和循環),3.理解函數的定義和使用,4.通過簡單示例和代碼片段快速上手Python編程。

Python在web開發、數據科學、機器學習、自動化和腳本編寫等領域有廣泛應用。 1)在web開發中,Django和Flask框架簡化了開發過程。 2)數據科學和機器學習領域,NumPy、Pandas、Scikit-learn和TensorFlow庫提供了強大支持。 3)自動化和腳本編寫方面,Python適用於自動化測試和系統管理等任務。

兩小時內可以學到Python的基礎知識。 1.學習變量和數據類型,2.掌握控制結構如if語句和循環,3.了解函數的定義和使用。這些將幫助你開始編寫簡單的Python程序。

如何在10小時內教計算機小白編程基礎?如果你只有10個小時來教計算機小白一些編程知識,你會選擇教些什麼�...

使用FiddlerEverywhere進行中間人讀取時如何避免被檢測到當你使用FiddlerEverywhere...

Python3.6環境下加載Pickle文件報錯:ModuleNotFoundError:Nomodulenamed...


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver Mac版
視覺化網頁開發工具

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),