Python 自學第十一天:Class 類別

AdSense

類別

Class 類別主要用來定義封裝的變數和函式 (又稱作屬性)。定義類別名稱時,習慣首字大寫。可以分成靜態類別、一般類別和兩者混用類別。

靜態類別,不需要實體物件

定義

在 Class 中定義屬性,不需要 self 關鍵字。

基本語法如下:

1
2
3
4
class 類別名稱:
變數
def 函式名稱():
# ...

舉例:

1
2
3
4
5
6
7
8
9
class MyDog:
types = ["哈士奇", "柴犬", "馬爾濟斯", "薩摩耶"]
def show(dog):
if dog == "哈士奇":
print("會跳火圈")
elif dog == "馬爾濟斯":
print("會坐下")
else:
print("什麼都不會")

使用

定義完之後,直接使用 類別名稱.屬性名稱 呼叫屬性。

1
2
3
4
5
6
7
8
my_dogs = MyDog.types
print(my_dogs)
MyDog.show("柴犬")
MyDog.show("哈士奇")

# ['哈士奇', '柴犬', '馬爾濟斯', '薩摩耶']
# 什麼都不會
# 會跳火圈

一般類別,需要實體物件

定義

在 Class 中定義

  1. 創造實體的初始化函式,使用 __init__() 和 self 關鍵字。在區塊中定義物件的變數 (需要物件才能呼叫的變數)。
  2. 會和物件綁在一起的函式,需要 self 關鍵字。也就是物件的函式 (需要物件才能呼叫的函式)。

基本語法如下:

1
2
3
4
5
6
7
8
class 類別名稱:
def __init__(self, 參數1, 參數2, ...): # 創造實體的初始化函式
self.參數x = 參數1 # 物件的變數1
self.參數y = 參數2 # 物件的變數2
# ...

def 函式名稱(self, 參數a, 參數b, ...): # 物件的函式
# ...

舉例:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog:
def __init__(self, types, size, age): # 第一個參數一定要寫 self
self.types = types
self.size = size
self.age = age

def show(self): # 第一個參數一定要寫 self,才會和物件綁在一起
if self.types == "哈士奇":
print("會跳火圈")
elif self.types == "馬爾濟斯":
print("會坐下")
else:
print("什麼都不會")

使用

定義完之後:

  1. 先利用 自訂物件名稱 = 類別名稱(參數1, 參數2, ...) 創造物件實體。例如:my_dog = Dog(types參數, size參數, age參數)。不需要傳入 self參數。

  2. 再使用 自訂物件名稱.變數、自訂物件名稱.函式(參數a, ...) 呼叫物件的屬性。不需要傳入 self參數。

1
2
3
4
5
6
7
8
9
10
11
12
dog1 = Dog("哈士奇", "大型犬", 2)          # 創造物件實體
print(dog1.types + " is " + dog1.size) # 呼叫變數
dog1.show() # 呼叫函式

dog2 = Dog("柴犬", "中型犬", 1)
print(dog2.types + " is " + dog2.size)
dog2.show()

# 哈士奇 is 大型犬
# 會跳火圈
# 柴犬 is 中型犬
# 什麼都不會

值得注意的是,如果我使用 類別名稱.屬性名稱 會出現如下錯誤:

1
2
3
4
5
6
7
Dog.types
# AttributeError: type object 'Dog' has no attribute 'types'
# Dog 這個類別沒有 'types' 這個屬性。

Dog.show()
# TypeError: show() missing 1 required positional argument: 'self'
# show 缺少一個必要的參數 'self'

咦!咦!

  1. 為什麼 Dog 類別沒有 types 屬性?
    因為 types 是物件的變數,show() 也是物件的函式。如果用類別呼叫,程式無法找到類別的 types 和 show(),自然就會報錯。

  2. 前面不是提到,不需要傳入 self參數,為什麼又說少一個必要的參數 self 呢?
    因為定義函式時,第一個參數 self 其實表示的是物件實體。如下圖,使用 dog1. 呼叫 show() 時,就表示 dog1 被當作 self參數 傳入。

所以想使用 類別名稱 呼叫函式,需要傳入 self 參數,改寫成這樣:

1
2
3
dog1 = Dog("哈士奇", "大型犬", 2)
print(dog1.types + " is " + dog1.size)
Dog.show(dog1) # 使用「類別名稱」呼叫函式,需要傳入 self 參數

兩者混用

在 Class 中定義屬性:想要和物件綁在一起的變數和函式就加 self 關鍵字。反之,則不加。直接來看以下的舉例。

定義

在狗的類別中:

  • 定義只有四種狗屬於寵物狗,以 Set 型態表示。不限定於某個狗實體才有的特徵,以 Dog.pet_types 或 物件.pet_types 呼叫。
  • 定義創造實體的初始化函式,使用 __init__() 和 self 關鍵字,在區塊中定義物件的變數。限定於某個狗實體才有的特徵,以 物件.變數名稱 呼叫。
  • 定義只要是狗都會吠叫的函式 bark()。不限定於某個狗實體才會的功能,以 Dog.bark() 呼叫。
  • 定義會和物件綁在一起的函式 show(),需要 self 關鍵字,也就是物件的函式。限定於某個狗實體才會的技能,以 物件.show() 呼叫。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Dog:
pet_types = {"哈士奇", "柴犬", "馬爾濟斯", "薩摩耶"}

def __init__(self, types, size, age): # 第一個參數一定要寫 self
self.types = types
self.size = size
self.age = age

def bark():
print("汪!汪!汪!")

def show(self): # 第一個參數一定要寫 self,才會和物件綁在一起
if self.types not in Dog.pet_types:
print("不是寵物狗")
if self.types == "哈士奇":
print("會跳火圈")
elif self.types == "馬爾濟斯":
print("會坐下")
else:
print("什麼都不會")

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
print(Dog.pet_types)   # 寵物狗的類型
Dog.bark() # 每種狗都會叫,使用「Dog.」呼叫

my_dog1 = Dog("哈士奇", "大型犬", 2)
print(my_dog1.types + " is " + my_dog1.size)
my_dog1.show()

his_dog2 = Dog("野狗", "中型犬", 3)
print(his_dog2.types + " is " + his_dog2.size)
his_dog2.show()


# {'哈士奇', '薩摩耶', '馬爾濟斯', '柴犬'}
# 汪!汪!汪!
#
# 哈士奇 is 大型犬
# 會跳火圈
#
# 野狗 is 中型犬
# 不是寵物狗
# 什麼都不會

注意,非物件的變數以 類別.pet_types 或 物件.pet_types 呼叫都可以。可是,非物件的函式以 物件.bark() 呼叫會報錯。

1
2
his_dog2.bark()
# TypeError: bark() takes 0 positional arguments but 1 was given

它說因為 bark() 不需要參數,但是我有給它一個參數 his_dog2物件,所以不對。

如果希望 實體物件 也可以使用 物件名稱 呼叫非物件的函式,也就是靜態函式,則定義時要在函式上方增加 @staticmethod ,如下:

1
2
3
4
5
6
class Dog:
# ...

@staticmethod
def bark():
print("汪!汪!汪!")

因為有明確告知 python,bark() 是靜態函式,即使使用 物件名稱 呼叫,也可以達成。

1
2
3
4
his_dog2.bark()

# 輸出結果:
# 汪!汪!汪!

參考資料:
彭彭的課程:Python 類別的定義與使用
彭彭的課程:Python 實體物件的建立與使用 - 上篇 - 實體屬性 Instance Attributes
彭彭的課程:Python 實體物件的建立與使用 - 下篇 - 實體屬性 Instance Attributes