在處理 Python 類別時,您會遇到名為 @classmethod 的裝飾器。這與 @staticmethod 或一般實例方法(Instance Method)有明確的區別,擁有獨特的目的。
在這篇文章中,我們將清楚了解 @classmethod 是什麼,以及在何種情況下如何使用它,並與 @staticmethod 和實例方法進行比較。
1. 三種方法類型:實例方法 vs. 類方法 vs. 靜態方法
Python 類別基本上有三種方法類型。它們最大的不同在於 方法接收的第一個參數是什麼。
class MyClass:
# 1. 實例方法 (Instance Method)
def instance_method(self, arg1):
# 'self' 代表類別的實例。
# 可以訪問和修改實例屬性(self.x)。
print(f"實例方法被調用,參數為 {self} 和 {arg1}")
# 2. 類方法 (Class Method)
@classmethod
def class_method(cls, arg1):
# 'cls' 代表類別本身(MyClass)。
# 主要用於訪問類屬性或作為創建實例的“工廠”。
print(f"類方法被調用,參數為 {cls} 和 {arg1}")
# 3. 靜態方法 (Static Method)
@staticmethod
def static_method(arg1):
# 不接受 'self' 或 'cls'。
# 是與類或實例狀態無關的輔助函數。
# 此函數只是在類內部“附屬”。
print(f"靜態方法被調用,參數為 {arg1}")
# 方法調用示例
obj = MyClass()
# 實例方法(通過實例調用)
obj.instance_method("Hello")
# 類方法(可以通過類別或實例調用)
MyClass.class_method("World")
obj.class_method("World") # 這樣也可以,但通過類別調用更為明確。
# 靜態方法(可以通過類別或實例調用)
MyClass.static_method("Python")
obj.static_method("Python")
一目瞭然的比較
| 類別 | 第一個參數 | 訪問對象 | 主要用途 |
|---|---|---|---|
| 實例方法 | self(實例) |
實例屬性 (self.name) |
操作或訪問對象的狀態 |
| 類方法 | cls(類別) |
類屬性 (cls.count) |
替代建構子(工廠) |
| 靜態方法 | 無 | 無 | 與類邏輯相關的輔助函數 |
2. @classmethod 的核心應用:工廠方法
使用 @classmethod 的最重要且普遍的案例是定義 “替代建構子(Alternative Constructor)” 或 “工廠方法(Factory Method)”。
基本構造函數 __init__ 通常接受明確的參數以初始化對象。然而,有時需要以不同的方式來創建對象(例如:字串解析、字典、特定計算值)。
使用 @classmethod,可以創建除了 __init__ 之外的另一個返回 類別實例 的“進入點”。

示例:根據日期字串創建 Person 對象
假設 Person 類別接受 name 和 age。若要根據 '名字-出生年份' 格式的字串來創建對象,該怎麼做呢?
import datetime
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"你好,我是 {self.name},今年 {self.age} 歲。")
@classmethod
def from_birth_year(cls, name, birth_year):
"""
根據出生年份計算年齡並創建 Person 實例。
"""
current_year = datetime.datetime.now().year
age = current_year - birth_year
# 'cls' 指的是 Person 類別本身。
# return Person(name, age) 是等價的,但使用 'cls' 在繼承時會有優勢(詳見下文)。
return cls(name, age)
# 使用基本構造函數
person1 = Person("Alice", 30)
person1.greet()
# 使用 @classmethod 創建的替代建構子
person2 = Person.from_birth_year("MadHatter", 1995)
person2.greet()
輸出結果:
你好,我是 Alice,今年 30 歲。
你好,我是 MadHatter,今年 30 歲。(根據當前年份 2025)
在上面的示例中,from_birth_year 方法通過 cls 參數訪問 Person 類別本身。然後利用計算得出的 age 來調用 cls(name, age),這等同於調用 Person(name, age) 並返回一個新的 Person 實例。
3. @classmethod 與繼承
“直接調用 Person(...) 不就夠了,為什麼還要使用 cls(...)?”
@classmethod 的真正威力在於 繼承。cls 代表了當前方法被調用的 _那個類別_。
假設我們創建一個繼承 Person 的 Student 類別。
class Student(Person):
def __init__(self, name, age, major):
super().__init__(name, age) # 調用父類的 __init__
self.major = major
def greet(self):
# 方法重寫
print(f"你好,我是 {self.major} 主修的 {self.name},今年 {self.age} 歲。")
# 在 Student 類別中調用父類的 @classmethod。
student1 = Student.from_birth_year("Jhon", 2005, "計算機科學")
# 唉,發生 TypeError!
# 當調用 Student.from_birth_year 時,
# 會執行 Person.from_birth_year,並且這時 'cls' 會是 'Student'。
# 亦即,return cls(name, age) 會成為 return Student(name, age)。
# 但 Student 的 __init__ 需要額外的 'major' 參數。
上述代碼由於 Student 的 __init__ 需要 major,因此會引發錯誤。但關鍵點在於 cls 指向的不是 Person 而是 Student。
如果 from_birth_year 是 @staticmethod 或在內部硬編碼為 Person(name, age),那麼調用 Student.from_birth_year 將會返回 Person 對象,而不是 Student 對象。
@classmethod 通過 cls 確保即使子類調用,也能生成正確的子類實例。(當然,如上述示例,若子類的 __init__ 簽名變更,也必須覆寫 from_birth_year。)
4. 總結
簡而言之,@classmethod 可以總結為如下幾點:
-
@classmethod接受cls(類別本身)作為第一參數。 -
主要用途是“替代建構子(工廠方法)”。 在需要提供創建對象的多種方法時使用。
-
在與繼承一起使用時特別強大。 在子類中調用時,
cls會指向正確的子類,從而創建該類別的實例。
記住 @staticmethod 用於完全不需要類/實例狀態的輔助函數,@classmethod 用於需要訪問類本身的工廠方法,普通實例方法則用於處理對象的狀態 (self),這樣就可以更清晰且符合 Python 風格地編寫代碼。
目前沒有評論。