Python 類別介紹,以及定義和使用 Python 類別
先決條件
閱讀本節的先決條件是已經掌握類別的相關概念,你可以檢視程式設計教學的物件導向程式設計,類別,執行個體介紹一節來了解他們。
定義 Python 類別
在 Python 中,類別通常被定義在模組中,其語法的基本形式如下。
class <classname>:
<docstring>
<block>
- classname 部分
classname
為 Python 類別的名稱,他需要符合 Python 的識別碼規格,不能使用關鍵字或保留關鍵字。- docstring 部分
docstring
是 Python 類別的文件字串,包含了對類別的說明,這並非強製性的要求,一個 Python 類別可以沒有文件字串。- block 部分
block
為 Python 類別的主體程式碼,他和docstring
都需要使用某種空白字元進行縮排,以表示其歸屬於 Python 類別。
Python 類別的特性
與模組類似,在 Python 類別中定義的變數(欄位),屬性,方法(函式)等內容,被稱為特性(Attribute)。特性可通過形式類似於c.name
,c.name=value
的運算式進行存取,其中c
為 Python 類別或物件(類別的執行個體),name
為特性(名稱,識別碼),value
為特性的新值。
當然,Python 類別的特性可以被稱呼為變數,方法,屬性,這並沒有什麽問題。
下面是一個簡單的 Python 類別Apple
,包含變數和靜態方法。
# 一個簡單的類別 Apple
class Apple:
# 變數 variety
variety = '普通蘋果'
# 靜態方法 show
@staticmethod
def show():
print(Apple.variety)
# 為特性 variety 指派
Apple.variety = '石頭蘋果'
# 呼叫 show 方法
Apple.show()
石頭蘋果
Python 的類別特性和執行個體特性
在 Python 中,類別的特性分為類別特性和執行個體特性,類別特性直接在 Python 類別中定義,或通過表示類別的參數(一般約定為cls
)來定義,而執行個體特性則需要通過 Python 執行個體,或表示執行個體的參數(一般約定為self
)來定義。
Python 類別特性可通過類別或表示類別的參數(一般約定為cls
),以及 Python 執行個體或表示執行個體的參數(一般約定為self
)來存取,而 Python 執行個體特性則只能通過執行個體或表示執行個體的參數來存取。
為 Python 類別或執行個體新增暫時特性
在沒有限製的情況下,一個 Python 類別或執行個體可以新增暫時存在的特性,只需要使用形式為c.name=value
的運算式即可,其中c
為類別或執行個體,name
為新增的特性(名稱,識別碼),value
為特性的值。
如何檢視 Python 類別或執行個體的所有特性?
通過 Python 類別,執行個體,物件的特殊特性__dict__
,你可以檢視其對應的所有特性,包括私用特性和暫時特性在內。__dict__
是一個字典物件,其中鍵值組的鍵表示特性的名稱,鍵值組的值表示特性的具體內容。
需要指出的是,Python 類別的__dict__
與執行個體的__dict__
中的鍵值組不會重疊,類別的__dict__
僅包含類別特性,執行個體的__dict__
僅包含執行個體特性,不過,不排除存在分別歸屬於 Python 類別和執行個體的同名特性。
我們調整之前的範例,為類別Apple
新增建構子以定義執行個體變數weight
,並通過類別新增暫時類別變數price
,通過執行個體新增暫時執行個體變數price
,然後輸出顯示Apple
類別和執行個體的__dict__
特性。
# 一個簡單的類別 Apple
class Apple:
# …
# 建構子
def __init__(self, w):
# 通過表示執行個體的 self 定義了執行個體變數 weight
self.weight = w
# …
# 為 Apple 類別新增一個類別變數,然後顯示 Apple 類別的所有特性
Apple.price = 10
print(Apple.__dict__)
# 為執行個體新增一個執行個體變數,然後顯示執行個體的所有特性
apple = Apple(100)
apple.price = 30
print(apple.__dict__)
{'__module__': '__main__', 'variety': '石頭蘋果', 'show': <staticmethod(<function Apple.show at …>)>, '__init__': <function Apple.__init__ at …>, '__dict__': <attribute '__dict__' of 'Apple' objects>, '__weakref__': <attribute '__weakref__' of 'Apple' objects>, '__doc__': None, 'price': 10}
{'weight': 100, 'price': 30}
不能通過 Python 執行個體來直接指派修改類別特性
你可以通過 Python 執行個體或表示執行個體的參數(比如self
)來讀取類別特性,但無法進行指派操作,因為指派操作將被認為是向 Python 執行個體增加新的特性,這類似於試圖在函式中直接指派模組定義的變數。
在下面的程式碼中,書寫apple.variety
等同於讀取Apple.variety
,書寫apple.variety='超大蘋果'
等同於為執行個體apple
新增執行個體變數variety
,Apple.variety
不會被修改。
# …
# 建立 Apple 類別的執行個體
apple = Apple(30)
# 通過執行個體存取類別變數 variety
print(apple.variety)
# 下面的指派陳述式將為執行個體定義新的執行個體變數 variety
apple.variety = '超大蘋果'
# 因此,類別變數 variety 並沒有變化
print(Apple.variety)
石頭蘋果
石頭蘋果
定義 Python 類別的變數
Python 類別的變數(欄位)分為類別變數和執行個體變數,他是 Python 類別的特性之一,你可以通過以下形式來定義類別變數或執行個體變數,其中,cls
為表示 Python 類別的參數,self
為表示 Python 執行個體的參數。
[cls|self.]<variablename>=<value>
- variablename 部分
variablename
為變數名稱,他需要符合 Python 的識別碼規格,不能使用 Python 關鍵字或保留關鍵字。- value 部分
value
為變數對應的值。
# 一個表示學生的類別 Student
class Student:
# 類別變數 count
count = 0
# 建構子
def __init__(self, n, a):
# 執行個體變數 name,age
self.name = n
self.age = a
定義 Python 類別的方法
Python 類別的方法(函式)是類別的特性之一,他與 Python 模組中的函式有相似之處,可通過如下形式進行定義,其中,@classmethod
表示定義類別方法,@staticmethod
表示定義靜態方法。
[@classmethod|@staticmethod]
def <methname>(<parameterlist>)
<block>
- methname 部分
methname
為方法名稱,他需要符合 Python 的識別碼規格,不能使用 Python 關鍵字或保留關鍵字。- parameterlist 部分
parameterlist
是方法的參數串列,他定義了方法的所有參數,參數之間使用,
進行分隔。- block 部分
block
為方法的主體程式碼,需要使用某種空白字元進行縮排,以表示其歸屬於方法。
如果未指定修飾詞@classmethod
或@staticmethod
,則定義的方法被稱為執行個體方法,其第一個參數一般命名為self
,表示 Python 執行個體本身,通過該參數可讀取寫入執行個體特性,或讀取類別特性。當通過 Python 執行個體呼叫執行個體方法時,需要省去self
參數,當通過 Python 類別呼叫執行個體方法時,需要給出self
參數。
如果指定了修飾詞@classmethod
,則定義的方法為類別方法,其第一個參數一般命名為cls
,表示 Python 類別本身,通過該參數可以存取類別特性,但不能存取執行個體特性。無論是通過 Python 執行個體還是 Python 類別,呼叫類別方法時都需要省去cls
參數。
如果指定了修飾詞@staticmethod
,則定義的方法為靜態方法,靜態方法不像執行個體方法或類別方法一樣擁有特殊參數,因此他不能通過特殊參數來存取 Python 執行個體特性或類別特性。
直接定義在 Python 類別中的方法均為類別特性
事實上,直接定義在 Python 類別中的方法均為類別特性,而非執行個體特性,無論這些方法被稱為執行個體方法,類別方法還是靜態方法,他們之間的主要區別在於,首個參數表示的是 Python 執行個體還是 Python 類別,或均不表示。
也正因為都是類別特性,執行個體方法,類別方法,靜態方法既可以通過 Python 執行個體呼叫,也可以通過 Python 類別呼叫。
函式
想要深入了解 Python 函式,你可以檢視Python 函式,參數,傳回值介紹,以及 Python 函式的定義和呼叫一節。
下面,我們為之前的Student
類別增加一些方法,並分別通過類別或執行個體來呼叫他們。
# 一個表示學生的類別 Student
class Student:
# …
# 執行個體方法 info
def info(self):
print(f'{self.name} {self.age}')
# 類別方法 show
@classmethod
def show(cls):
# 通過參數 cls 讀取類別變數 count
print(f'一共 {cls.count} 個學生')
# 靜態方法 set_count
@staticmethod
def set_count(c):
# 只能通過類別存取類別變數
Student.count = c
print(f'學生數量被設定為 {Student.count}')
student = Student('小小', 13)
# 呼叫執行個體方法 info
student.info()
Student.info(student)
# 呼叫靜態方法 set_count
student.set_count(-100)
Student.set_count(100)
# 呼叫類別方法 show
student.show()
Student.show()
小小 13
小小 13
學生數量被設定為 -100
學生數量被設定為 100
一共 100 個學生
一共 100 個學生
定義 Python 類別的私用特性
無論是 Python 類別特性還是執行個體特性,如果識別碼以__
開頭,則這些特性即為私用特性,只能在定義他們的 Python 類別中存取,不能在外部或衍生類別中存取。
Python 類別的私用特性識別碼會自動增加首碼
以__
開頭的私用特性,其識別碼會自動增加特定首碼,其內容為_
與類別名稱的組合,這一現象可通過 Python 類別或 Python 執行個體的__dict__
特性來觀察。識別碼的改動僅涉及定義特性的類別,這也就是無法在外部或衍生類別中使用私用特性的原因,因為你在存取一個並不存在的內容。
當然,在了解以上規則後,私用特性可能變得不再私用,只需要采用具有特定首碼的識別碼進行存取即可。
下面的類別Teacher
定義了兩個私用的執行個體變數,他們的識別碼自動新增了首碼_Teacher
。
# 一個表示教師的類別 Teacher
class Teacher:
# 建構子
def __init__(self, n, a):
# 私用執行個體變數 __name,__age
self.__name = n
self.__age = a
teacher = Teacher('隱者', 30)
# 顯示 Teacher 執行個體的所有特性
print(teacher.__dict__)
# 使用特殊方式存取私用變數 __name
print(teacher._Teacher__name)
{'_Teacher__name': '隱者', '_Teacher__age': 30}
隱者
定義 Python 類別的建構子
Python 類別的建構子是一個名為__init__
的執行個體方法,與其他執行個體方法一樣,__init__
方法的第一個參數一般命名為self
,表示 Python 執行個體自身,其余參數可用於初始化執行個體變數或執行其他操作,就如同Student
類別所作的那樣。
當然,在 Python 的官方文件中,並沒有建構子這樣的說法,__init__
的含義僅為初始化,他在 Python 執行個體建立後被呼叫。
建立 Python 類別的執行個體
要建立 Python 類別的執行個體,你需要使用類別名稱和()
,()
包含了一組參數,這些參數一般與__init__
方法的參數一致(需要省去self
)。比如,之前範例中使用Student('小小',13)
建立了Student
類別的執行個體。
如何檢視 Python 執行個體對應的型別?
通過 Python 執行個體(物件)的__class__
屬性(Property),可以取得該執行個體對應的型別(類別)資訊,__class__
屬性的傳回值是一個type
物件。
對於已定義的 Python 類別來說,__class__
屬性的傳回值顯示為<class 'type'>
,這表示已定義的 Python 類別可被視為執行個體,其型別是type
。
# …
# 取得 Student 類別和之前建立的執行個體 student 的型別資訊
print(student.__class__)
print(Student.__class__)
<class '__main__.Student'>
<class 'type'>
Python 類別的命名空間
理所當然的,Python 類別應該擁有自己的命名空間,以避免類別所擁有的特性的識別碼與其他識別碼沖突。
命名空間,有效範圍
想要深入了解命名空間和有效範圍,你可以檢視程式設計教學的命名空間,有效範圍介紹一節。
Python 類別的文件字串
與 Python 函式,模組類似,Python 類別也具有儲存在__doc__
中的文件字串,他是字串常值,是類別的第一行有效程式碼,可以采用緊密相鄰或加入空白字元的方式進行拼接,不包含需要運算的內容或操作。
# 一個奇怪的類別
class Hello:
"""你好啊,"""'Python!'
Python 巢狀類別
巢狀類別是定義在另一個類別,函式或方法中的 Python 類別。由於有效範圍的一般性原則,被定義的巢狀類別的可存取性,等同於同一命名空間中定義的 Python 變數。
下面定義的巢狀類別World
,其可存取性等同於Hello
中的類別變數。
# 一個奇怪的類別
class Hello:
# …
# 一個奇怪的巢狀類別
class World:
pass