靜態方法和類方法
靜態方法
我們在類中定義的方法都是對象方法,也就是說這些方法都是發送給對象的消息。實際上,我們寫在類中的方法並不需要都是對象方法,例如我們定義一個“三角形”類,通過傳入三條邊長來構造三角形,並提供計算周長和面積的方法,但是傳入的三條邊長未必能構造出三角形對象,因此我們可以先寫一個方法來驗證三條邊長是否可以構成三角形,這個方法很顯然就不是對象方法,因為在調用這個方法時三角形對象尚未創建出來(因為都不知道三條邊能不能構成三角形),所以這個方法是屬於三角形類而並不屬於三角形對象的。我們可以使用靜態方法來解決這類問題,代碼如下所示。
from math import sqrt
class Triangle(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
def perimeter(self):
return self._a + self._b + self._c
def area(self):
half = self.perimeter() / 2
return sqrt(half * (half - self._a) *
(half - self._b) * (half - self._c))
def main():
a, b, c = 3, 4, 5
# 靜態方法和類方法都是通過給類發消息來調用的
if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
# 也可以通過給類發消息來調用對象方法但是要傳入接收消息的對象作為參數
# print(Triangle.perimeter(t))
print(t.area())
# print(Triangle.area(t))
else:
print('無法構成三角形.')
if __name__ == '__main__':
main()
類方法
和靜態方法比較類似,Python還可以在類中定義類方法,類方法的第一個參數約定名為cls,它代表的是當前類相關的信息的對象(類本身也是一個對象,有的地方也稱之為類的元數據對象),通過這個參數我們可以獲取和類相關的信息並且可以創建出類的對象,代碼如下所示。
from time import time, localtime, sleep
class Clock(object):
"""數字時鍾"""
def __init__(self, hour=0, minute=0, second=0):
self._hour = hour
self._minute = minute
self._second = second
@classmethod
def now(cls):
ctime = localtime(time())
return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)
def run(self):
"""走字"""
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0
def show(self):
"""顯示時間"""
return '%02d:%02d:%02d' % \
(self._hour, self._minute, self._second)
def main():
# 通過類方法創建對象並獲取系統時間
clock = Clock.now()
while True:
print(clock.show())
sleep(1)
clock.run()
if __name__ == '__main__':
main()
靜態方法、類方法使用區別或者說使用場景
1、類方法用在模擬java定義多個構造函數的情況。
由於python類中只能有一個初始化方法,不能按照不同的情況初始化類。
參考django https://docs.djangoproject.com/en/1.9/ref/models/instances/ 請看下面的代碼。
# coding:utf-8
class Book(object):
def __init__(self, title):
self.title = title
@classmethod
def create(cls, title):
book = cls(title=title)
return book
book1 = Book("python")
book2 = Book.create("python and django")
print(book1.title)
print(book2.title)
特別說明,靜態方法也可以實現上面功能,當靜態方法每次都要寫上類的名字,不方便。
2、類中靜態方法方法調用靜態方法的情況。
下面的代碼,靜態方法調用另一個靜態方法,如果改用類方法調用靜態方法,可以讓cls代替類,
讓代碼看起來精簡一些。也防止類名修改了,不用在類定義中修改原來的類名。
# coding:utf-8
class Foo(object):
X = 1
Y = 2
@staticmethod
def averag(*mixes):
return sum(mixes) / len(mixes)
@staticmethod
def static_method():
return Foo.averag(Foo.X, Foo.Y)
@classmethod
def class_method(cls):
return cls.averag(cls.X, cls.Y)
foo = Foo()
print(foo.static_method())
print(foo.class_method())
3、繼承類中的區別
從下面代碼可以看出,如果子類繼承父類的方法,子類覆蓋了父類的靜態方法,
子類的實例繼承了父類的static_method靜態方法,調用該方法,還是調用的父類的方法和類屬性。
子類的實例繼承了父類的class_method類方法,調用該方法,調用的是子類的方法和子類的類屬性。
# coding:utf-8
class Foo(object):
X = 1
Y = 2
@staticmethod
def averag(*mixes):
return sum(mixes) / len(mixes)
@staticmethod
def static_method():
return Foo.averag(Foo.X, Foo.Y)
@classmethod
def class_method(cls):
return cls.averag(cls.X, cls.Y)
class Son(Foo):
X = 3
Y = 5
@staticmethod
def averag(*mixes):
return sum(mixes) / 3
p = Son()
print(p.static_method())
print(p.class_method())
# 1.5
# 2.6666666666666665
