簡介類方法和實例方法
- 首先我們複習一下實例方法(instance-level method)
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def sayhi(self, v1):
return(f"Hi, my name is {self.name}, and I'm {self.age} year's old. {v1} is here")
def sayhi2(self, v1):
res = self.sayhi(v1)
return(res)
- 裡面的
sayhi(self, v1)就是一實例方法,因為他需要傳入self這個參數,好讓他在method裡面可以呼叫self.name, self.age這兩個instance-level attribute
- 而
sayhi2(self, v1)同樣是個實例方法,這邊就顯示我們要呼叫實例方法時,也得用self,如同這邊用到self.sayhi這個method
- 那舉一反三,class-level method,傳入的參數就該從
self改成class? 答案很接近了,用的參數名稱叫cls而不是class。而且,還要在定義method的前面,加個decorator: @classmethod
- 看例子:
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def sayhi(self, v1):
return(f"Hi, my name is {self.name}, and I'm {self.age} year's old. {v1} is here")
def sayhi2(self, v1):
res = self.sayhi(v1)
return(res)
@classmethod
def test1(cls):
return("這是一個類方法")
- 那定義好這個
類方法後,我們從外部就不需要先realize一個instance,就可以用這個方法了:
print(People.test1())
#> 這是一個類方法
- 而且,如同實例屬性會繼承類屬性一樣,實例方法也會繼承類方法,所以當我realize一個instance後,我還是可以調用這個method:
p1 = People(name = "hank", age = 28)
print(p1.test1())
#> 這是一個類方法
- 那最後講一下,instance-level method時,你傳入
self參數,是為了用self.name, self.age這些instance-level attribute,或是用self.sayhi()這種instance-level method。那class-level method時,你傳入cls參數,是為了調用cls.XXX的class-level attribute嗎?
- 顯然不是,因為class-level attribute的調用,是用
{class名稱}.{attribute名稱}。所以,cls參數要調用的,是其他class method!!
- 例如下例:
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def sayhi(self, v1):
return(f"Hi, my name is {self.name}, and I'm {self.age} year's old. {v1} is here")
def sayhi2(self, v1):
res = self.sayhi(v1)
return(res)
@classmethod
def test1(cls):
return("這是一個類方法")
@classmethod
def test2(cls):
res = cls.test1()
return(res)
- 從這個例子可以看到,我的
test2,調用了cls.test1這個class-level method。來看看結果對不對:
print(People.test2())
#> 這是一個類方法
- 最後,我們無法在類方法中,去調用實例方法。例如我現在寫個test3,裡面想要調用self.sayhi
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def sayhi(self, v1):
return(f"Hi, my name is {self.name}, and I'm {self.age} year's old. {v1} is here")
def sayhi2(self, v1):
res = self.sayhi(v1)
return(res)
@classmethod
def test1(cls):
return("這是一個類方法")
@classmethod
def test2(cls):
res = cls.test1()
return(res)
@classmethod
def test3(cls, v1):
res = self.sayhi(v1)
return(res)
print(People.test3(v1 = "hahaha"))
#> Error in py_call_impl(callable, dots$args, dots$keywords): NameError: name 'self' is not defined
#>
#> Detailed traceback:
#> File "<string>", line 1, in <module>
#> File "<string>", line 24, in test3
- 如同預期,報error了,而且error message也很合理:沒找到self拉!啊廢話,你這是class-level method,給的參數是
cls又不是self,當然無法去吃self.sayhi()這個instance-level method
- 而且,就算你實體化這個class,還是不能調用
self.sayhi(),因為你的self從頭到尾都沒被當作參數丟入test3裡面:
p2 = People(name = "hank", age = 28)
p2.test3(v1 = "hahaha")
#> Error in py_call_impl(callable, dots$args, dots$keywords): NameError: name 'self' is not defined
#>
#> Detailed traceback:
#> File "<string>", line 1, in <module>
#> File "<string>", line 24, in test3
實例屬性的使用時機
- 那到底class method可以拿來幹麻?其實最常是拿來作為initialize instance的另一種方式,例如,我的class如果本來要initialize一個instance時,要輸入year, month, day這三個參數,但我現在希望提供另一種initialize的方式,是直接給字串(e.g. “2020-10-20”)就好,我該怎麼做?只有一個
__init__可以用,又不能寫兩個,這時候,就會用class method
- 直接看例子。今天我想寫一個class叫
BetterDate,有year, month, day三個attribute。那我現在想寫一個class method,只要給字串,我一樣initialize instance給你:
class BetterDate:
# Constructor
def __init__(self, year, month, day):
# Recall that Python allows multiple variable assignments in one line
self.year, self.month, self.day = year, month, day
# Define a class method from_str
@classmethod
def from_str(cls, datestr):
# Split the string at "-" and convert each part to integer
parts = datestr.split("-")
year, month, day = int(parts[0]), int(parts[1]), int(parts[2])
# Return the class instance
return cls(year, month, day)
- 注意到最後的
cls(year, month, day),他的意思就是會把這三個參數傳回__init__來initialize instance的意思。所以,現在試試看結果:
bd = BetterDate.from_str('2020-04-30')
print(bd.year)
#> 2020
print(bd.month)
#> 4
print(bd.day)
#> 30