1. 程式人生 > >Python 面向物件 - @classmethod & @staticmethod

Python 面向物件 - @classmethod & @staticmethod

@classmethod和@staticmethod很像,但他們的使用場景並不一樣。

  • 類內部普通的方法,都是以self作為第一個引數,代表著通過例項呼叫時,將例項的作用域傳入方法內;
  • @classmethod以cls作為第一個引數,代表將類本身的作用域傳入。無論通過類來呼叫,還是通過類的例項呼叫,預設傳入的第一個引數都將是類本身
  • @staticmethod不需要傳入預設引數,類似於一個普通的函式

來通過例項瞭解它們的使用場景:

假設我們需要建立一個名為Date的類,用於儲存 年/月/日 三個資料

class Date(object):
    def
__init__(self, year=0, month=0, day=0):
self.year = year self.month = month self.day = day @property def time(self): return "{year}-{month}-{day}".format( year=self.year, month=self.month, day=self.day )

上述程式碼建立了Date類,該類會在初始化時設定day/month/year屬性,並且通過property設定了一個getter,可以在例項化之後,通過time獲取儲存的時間

date = Date('2016', '11', '09')
date.time # 2016-11-09

但如果我們想改變屬性傳入的方式呢?畢竟,在初始化時就要傳入年/月/日三個屬性還是很煩人的。能否找到一個方法,在不改變現有介面和方法的情況下,可以通過傳入2016-11-09這樣的字串來建立一個Date例項?

你可能會想到這樣的方法:

date_string = '2016-11-09'
year, month, day = map(str, date_string.split('-'))
date = Date(year, month, day)

但不夠好:

  • 在類外額外多寫了一個方法,每次還得格式化以後獲取引數
  • 這個方法也只跟Date類有關
  • 沒有解決傳入引數過多的問題

此時就可以利用@classmethod,在類的內部新建一個格式化字串,並返回類的例項的方法:

# 在 Date 內新增一個 classmethod
@classmethod
def from_string(cls, string):
    year, month, day = map(str, string.split('-'))
    # 在 classmethod 內可以通過 cls 來呼叫到類的方法,甚至建立例項
    date = cls(year, month, day)
    return date

這樣,我們就可以通過Date類來呼叫from_string方法建立例項,並且不侵略、修改舊的例項化方式:

date = Date.from_string('2016-11-09')
# 舊的例項化方式仍可以使用
date_old = Date('2016', '11', '09')

好處:

  • 在@classmethod內,可以通過cls引數,獲取到跟外部呼叫類時一樣的便利
  • 可以在其中進一步封裝該方法,提高複用性
  • 更加符合面向物件的程式設計方式

而@staticmethod,因為其本身類似於普通的函式,所以可以把和這個類相關的 helper 方法作為@staticmethod,放在類裡,然後直接通過類來呼叫這個方法。

# 在 Date 內新增一個 staticmethod
@staticmethod
def is_month_validate(month):
    return int(month) <= 12 and int(month) >= 1

將與日期相關的輔助類函式作為@staticmethod方法放在Date類內後,可以通過類來呼叫這些方法:

month = '08'
if not Date.is_month_validate(month):
    print('{} is a validate month number'.format(month))