python中@classmethod和@staticmethod的理解

https://eclipsesv.com/2017/08/03/關於python的@classmethod和@staticmethod的理解/

一直對python中@classmethod和@staticmethod的用法和區別不是很理解,一頓google之後發現還是stackoverflow真的是程序員的好朋友.在這個問題Meaning of @classmethod and @staticmethod for beginner?中,幾個大神的回答還是很好的.

@Rostyslav Dzinko這樣說 雖然classmethod和staticmethod十分相似,但他們之間確實存在一些細微的差別:classmethod必須有一個類對象作爲方法的第一個參數,然而staticmethod卻可以不需要參數. 下面👇,舉個例子🌰:

樣板代碼 假設我們需要一個類來處理和日期相關的信息:

  
  
  
  1. class Date(object):

  2.    def __init__(self, day=0, month=0, year=0):

  3.        self.day = day

  4.        self.month = month

  5.        self.year = year

顯然,這個類能夠存儲一些日期相關的信息,通過init方法,我們傳入day,month和year能夠實例化一個Date對象,其中init方法的第一個參數self就代表我們新建的Date實例.

Class Method 通過@classmethod我們可以比較優雅的完成一些任務.如果僅僅通過init方法來完成Date類的實例化,就必須這樣實現:x = Date(day,month,year).如果現在想要將一個日期的字符串形式(‘dd-mm-yy’)轉爲Date對象,我們需要完成這兩個步驟:

將字符串轉爲day,month,year三個整型對象或者一個包含三個值的元組; 通過init方法完成Date對象的實例化. 上邊的兩步實現過程就像這樣:

  
  
  
  1. day, month, year = map(int, string_date.split('-'))

  2. date1 = Date(day, month, year)

在其他編程語言中,以c++爲例,它可以重構自己的構造函數來接受某個日期的字符串形式最終返回一個Date實例.但是python沒有這樣的特性,於是classmethod就在這裏派上了用場:

  
  
  
  1. @classmethod

  2.    def from_string(cls, date_as_string):

  3.        day, month, year = map(int, date_as_string.split('-'))

  4.        date1 = cls(day, month, year)

  5.        return date1

  6. date2 = Date.from_string('11-09-2012')

上邊利用classmethod來完成將字符串轉爲Date實例主要有這些優勢:

將字符串轉化的過程放在類中,並且能夠重用; 封裝的較好,符合面向對象思想; classmethod中的cls代表Date,它不是類的一個實例,就是類對象本身,如果我們派生了其他的子類,它們也都能繼承fromstring方法. Static Method 說完了classmethod,接着嘮一嘮staticmethod.它其實和classmethod十分相似,但是它不需要像類方法或者普通的實例方法一樣需要必須的參數(cls或者self). 再舉個例子🌰: 通過classmethod我們完成了將一個字符串轉爲Date實例的過程,現在給我們一個字符串,在使用Date.fromstring(‘str’)生成實例之前,判斷這個str是否滿足要求. 很顯然,這個方法和類Date有密切的聯繫,但僅僅判斷一個字符串是否滿足轉換的要求,並不需要實例化一個Date對象,這時候staticmethod就可以派上用場:

  
  
  
  1. @staticmethod

  2.    def is_date_valid(date_as_string):

  3.        day, month, year = map(int, date_as_string.split('-'))

  4.        return day <= 31 and month <= 12 and year <= 3999

  5. # usage:

  6. is_date = Date.is_date_valid('11-09-2012')

也就是說,staticmethod可以像一個普通的方法被調用,它與這個類有明確的相關性,但是不需要訪問這個類內部的屬性或者方法.

@Yaw Boakye補充說 Rostyslav Dzinko說的非常好,不過他(Yaw Boakye)從另外一個方面對classmethod和staticmethod的區別做了補充: 在上邊的例子中,使用@classmethod from_string作爲一個生成Date實例的工廠,然而通過@staticmethod也可以完成類似的操作:

  
  
  
  1. class Date:

  2.    def __init__(self, month, day, year):

  3.        self.month = month

  4.        self.day   = day

  5.        self.year  = year

  6.      def display(self):

  7.        return "{0}-{1}-{2}".format(self.month, self.day, self.year)

  8.      @staticmethod

  9.      def millenium(month, day):

  10.        return Date(month, day, 2000)

  11. new_year = Date(1, 1, 2013)               # Creates a new Date object

  12. millenium_new_year = Date.millenium(1, 1) # also creates a Date object.

  13. # Proof:

  14. new_year.display()           # "1-1-2013"

  15. millenium_new_year.display() # "1-1-2000"

  16. isinstance(new_year, Date) # True

  17. isinstance(millenium_new_year, Date) # True

通過@staticmethod millenium我們現在也可以生成類似於工廠函數@classmethod from_string的功能,生成Date實例.但是這樣的實現卻有點硬編碼的嫌疑,因爲@staticmethod millenium的最終返回結果是通過return Date(month, day, 2000)這句代碼實現的,也就是說它明確了返回對象就是就是一個Date的實例. 下面我們派生一個Date的子類DateTime:

  
  
  
  1. class DateTime(Date):

  2.  def display(self):

  3.      return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year)

  4. datetime1 = DateTime(10, 10, 1990)

  5. datetime2 = DateTime.millenium(10, 10)

  6. datetime1.display() # returns "10-10-1990 - 00:00:00PM"

  7. datetime2.display() # returns "10-10-2000"

我們可以看到datetime1.display()的返回值"10-10-1990 - 00:00:00PM"而datetime2.display()的返回值是returns "10-10-2000",這是怎麼回事?原因在於datetime1和datetime2實例創建的方法是不同的:

datetime1通過DateTime的init方法生成 isinstance(datetime1, DateTime) # True datetime2通過DateTime.millenium方法生成 isinstance(datetime2, DateTime) # False 這裏就可以看到@staticmethod和@classmethod的一些不同,將millenium使用@classmethod重寫:

  
  
  
  1. @classmethod

  2. def millenium(cls, month, day):

  3.    return cls(month, day, 2000)

  4. 這裏的cls替代了之前的Date,它可以是任何一個派生出來的子類,millenium返回的實例當然也是對應的cls的實例.

  5. datetime1 = DateTime(10, 10, 1990)

  6. datetime2 = DateTime.millenium(10, 10)

  7. isinstance(datetime1, DateTime) # True

  8. isinstance(datetime2, DateTime) # True

  9. datetime1.display() # "10-10-1990 - 00:00:00PM"

  10. datetime2.display() # "10-10-2000 - 00:00:00PM"

小小的總結一下 通過這兩個很精彩的解釋,現在對@classmethod和@staticmethod有了進一步的理解:

@classmethod,由於其強制要求有cls參數存在,可以更多的用於當作一個類實例工廠🏭,或者可以作爲一個可以用於派生類中的構造函數; @staticmethod,如果一個方法不需要使用類內部的屬性和方法,但確實和類有明確的相關性,它就可以使用@staticmethod來修飾.


本文分享自微信公衆號 - 我愛問讀書(wawds_)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章