classmethod, staticmethod, instance method in Python
超多超多的文章都在討論究竟 Python 裡頭的 staticmethod && staticmethod && instance method 差在哪邊?
可以參考 這篇, 這篇 或 這篇...(以上都是好幾千個 upvote 的 Q&A), 半夜睡不著覺 而且對於知識狂熱的份子可以去好好讀它...
底下就用 "我自己的理解" 來湊熱鬧 寫篇文章解說解說
Prerequest
- 對於 Python 物件導向要有某種程度的熟悉
- 對於
@staticmethod
&@classmethod
多多少少有種模糊的認識
範例
底下範例我打算寫個元件(類別), 你只要給它 'date 物件', 他就能告訴你:
- 這天所在的那個月的第一天
- 這天所在的那個月的最後一天
- 這天所在的那週的第一天的日期
- 這天所在的那週的最後一天的日期
1from datetime import timedelta, date
2
3class DateTimeTools:
4 def first_day_of_month(self, dt: date) -> date:
5 return date(dt.year, dt.month, 1)
6
7 def last_day_of_month(self, dt: date) -> date:
8 dt2 = date(dt.year + 1, 1, 1) if dt.month == 12 else date(dt.year, dt.month + 1, 1)
9 return date(dt2.year, dt2.month, 1) - timedelta(days=1)
10
11 def first_day_of_week(self, dt: date) -> date:
12 return dt - timedelta(days=dt.isoweekday() % 7)
13
14 def last_day_of_week(self, dt: date) -> date:
15 return self.first_day_of_week(dt=dt) + timedelta(days=6)
16
17dd = date(2021, 9, 21)
18
19a = DateTimeTools()
20print(a.first_day_of_month(dt=dd)) # 2020-09-01
21print(a.last_day_of_month(dt=dd)) # 2020-09-30
22print(a.first_day_of_week(dt=dd)) # 2020-09-20
23print(a.last_day_of_week(dt=dd)) # 2020-09-26
因為上面的範例使用上有點不方便(每次都要傳參數給裡頭的方法), 稍微修改一下後變成下面這樣
1from datetime import timedelta, date
2
3class DateTimeTools:
4 def __init__(self, dt: date):
5 self._dt = dt
6
7 def first_day_of_month(self) -> date:
8 return date(self._dt.year, self._dt.month, 1)
9
10 def last_day_of_month(self) -> date:
11 dt2 = date(self._dt.year + 1, 1, 1) if self._dt.month == 12 else date(self._dt.year, self._dt.month + 1, 1)
12 return date(dt2.year, dt2.month, 1) - timedelta(days=1)
13
14 def first_day_of_week(self) -> date:
15 return self._dt - timedelta(days=self._dt.isoweekday() % 7)
16
17 def last_day_of_week(self) -> date:
18 return self.first_day_of_week() + timedelta(days=6)
19
20dd = date(2020, 9, 21)
21
22a = DateTimeTools(dt=dd)
23print(a.first_day_of_month()) # 2020-09-01
24print(a.last_day_of_month()) # 2020-09-30
25print(a.first_day_of_week()) # 2020-09-20
26print(a.last_day_of_week()) # 2020-09-26
善用 實例屬性, 將來呼叫方法時, 就不用再輸入參數
然而使用上, 或許你會覺得不必這麼麻煩,
我們還是回到最一開始的那種情況, 我需要把什麼樣的日期作轉換, 當下再給他日期就好
只是每次都得實例化很不直覺, 改成更好用的方式不就好了?
1from datetime import timedelta, date
2
3
4class DateTimeTools:
5
6 @classmethod
7 def first_day_of_month(cls, dt: date) -> date:
8 return date(dt.year, dt.month, 1)
9
10 @classmethod
11 def last_day_of_month(cls, dt: date) -> date:
12 dt2 = date(dt.year + 1, 1, 1) if dt.month == 12 else date(dt.year, dt.month + 1, 1)
13 return date(dt2.year, dt2.month, 1) - timedelta(days=1)
14
15 @classmethod
16 def first_day_of_week(cls, dt: date) -> date:
17 return dt - timedelta(days=dt.isoweekday() % 7)
18
19 @classmethod
20 def last_day_of_week(cls, dt: date) -> date:
21 return DateTimeTools.first_day_of_week(dt=dt) + timedelta(days=6)
22
23
24dd = date(2020, 9, 21)
25
26print(DateTimeTools.first_day_of_month(dt=dd)) # 2020-09-01
27print(DateTimeTools.last_day_of_month(dt=dd)) # 2020-09-30
28print(DateTimeTools.first_day_of_week(dt=dd)) # 2020-09-20
29print(DateTimeTools.last_day_of_week(dt=dd)) # 2020-09-26
現在全透過 類別方法 來呼叫即可, 而類別名稱就只剩下把這些方法歸類到同一個地方的作用而已
此外, 也可以改成 靜態方法, 讓程式碼再少一些
1from datetime import timedelta, date
2
3
4class DateTimeTools:
5
6 @staticmethod
7 def first_day_of_month(dt: date) -> date:
8 return date(dt.year, dt.month, 1)
9
10 @staticmethod
11 def last_day_of_month(dt: date) -> date:
12 dt2 = date(dt.year + 1, 1, 1) if dt.month == 12 else date(dt.year, dt.month + 1, 1)
13 return date(dt2.year, dt2.month, 1) - timedelta(days=1)
14
15 @staticmethod
16 def first_day_of_week(dt: date) -> date:
17 return dt - timedelta(days=dt.isoweekday() % 7)
18
19 @staticmethod
20 def last_day_of_week(dt: date) -> date:
21 return DateTimeTools.first_day_of_week(dt=dt) + timedelta(days=6)
22
23
24dd = date(2020, 9, 21)
25
26print(DateTimeTools.first_day_of_month(dt=dd)) # 2020-09-01
27print(DateTimeTools.last_day_of_month(dt=dd)) # 2020-09-30
28print(DateTimeTools.first_day_of_week(dt=dd)) # 2020-09-20
29print(DateTimeTools.last_day_of_week(dt=dd)) # 2020-09-26
在這個範例裡面, @staticmethod
& @classmethod
看起來只差一點點, 這並不意味著他們是一樣的
後記
最後不免會有疑問, 那什麼時候用 staticmethod? 又啥時用 classmethod
我的回答是(非專業見解), 其實我不是非常確定有沒有哪些情境, 其中一種方式是絕對優於另一者
目前書上, 網路上 東看西看的理解, 多半都只是在說 類別方法多了個 cls
, 至於使用情境
目前依然是一頭霧水.
因此我最終的結論是, 能正常使用就好. 符合需求就好. 習慣就好. 符合團隊要求就好.
comments powered by Disqus