Python 自學第十一天:Class 類別

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() 也是物件的函式。如果用類別呼叫,程式無法找到類別的 typesshow(),自然就會報錯。

  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