昨天簡介類別屬性
(class attributes),今天則淺談類別方法
(class methods)。
筆者談類別方法的「方法」(註1),是從類別屬性切入。
class Tree():
count = 0 # 放在constructor外面的是class attributes。
total_age = 0
average_age = 0
def __init__(self, breed: str, age: int):
self.__breed = breed
self.__age = age
Tree.count += 1 # class attributes是用Tree.xxx而非self.xxx
Tree.total_age += self.age
Tree.average_age = round(Tree.total_age / Tree.count, 2)
... # 以下略
count
, total_age
和average_age
這幾個類別屬性都設成公開,所以主程式可以直接以方法.屬性
或物件.屬性
存取,沒用到property。看來還滿「方便」的。屬性盡量私有(private)
的封裝保護層級理論。怎辦?改為私有吧:class Tree():
__count = 0 # All class attributes are set to private.
__total_age = 0
__average_age = 0
def __init__(self, breed: str, age: int):
self.__breed = breed
self.__age = age
Tree.__count += 1 # class attributes是用Tree.xxx而非self.xxx
Tree.__total_age += self.age
Tree.__average_age = round(Tree.__total_age / Tree.__count, 2)
@property
def count(cls) -> int:
'''The __count property(getter).'''
return cls.__count
@property
def total_age(cls) -> int:
'''The __total_age property(getter).'''
return cls.__total_age
@property
def average_age(cls) -> float:
'''The __average_age property(getter).'''
return round(cls.__total_age / cls.__count, 2)
@property
def breed(self) -> str:
'''The breed property(getter).'''
return self.__breed
@property
def age(self) -> int:
'''The age property(getter).'''
return self.__age
類別屬性
都是用「物件.屬性」方式存取:
def show_count_and_average(count: int, total: int, average: float):
print(f'{count=:<10,}{total=:<10,}{average=:,.2f}')
trees = [Tree('Cedar', 1_520), Tree('oak', 357), Tree('phoebe', 1_806)]
for tree in trees:
# 注意:以下三個類別屬性都是用「物件.屬性」方式存取。
# 討論:如果改用「類別.屬性」存取呢?
show_count_and_average(count=tree.count, total=tree.total_age, average=tree.average_age)
輸出:
- 所謂「共享」就是透過「物件.屬性」的表示式存取。
- 也可以透過類別本身,即「類別.屬性」表示式直接存取。
類別屬性
:def show_count_and_average(count: int, total: int, average: float):
print(f'{count=:<8,}{total=:<10,}{average=:,.2f}')
trees = [Tree('Cedar', 1_520), Tree('oak', 357), Tree('phoebe', 1_806)]
try:
for tree in trees:
# 改用「類別.屬性」存取。
show_count_and_average(count=Tree.count, total=Tree.total_age, average=Tree.average_age)
except Exception as e:
print(str(e))
類別方法
(class methods)。class Tree():
__count = 0 # 放在constructor外面的是class attributes。
__total_age = 0
__average_age = 0
def __init__(self, breed: str, age: int):
self.__breed = breed
self.__age = age
Tree.__count += 1 # class attributes是用Tree.xxx而非self.xxx
Tree.__total_age += self.age
Tree.__average_age = round(Tree.__total_age / Tree.__count, 2)
@classmethod # 注意:要加上這個decorator。
@property
def count(cls) -> int:
'''The __count property(getter).'''
return cls.__count
@classmethod # @classmethod要在@property的前面。
@property
def total_age(cls) -> int:
'''The __total_age property(getter).'''
return cls.__total_age
@classmethod
@property
def average_age(cls) -> float:
'''The __average_age property(getter).'''
return round(cls.__total_age / cls.__count, 2)
@property
def breed(self) -> str:
'''The breed property(getter).'''
return self.__breed
@property
def age(self) -> int:
'''The age property(getter).'''
return self.__age
def show_count_and_average(count: int, total: int, average: float):
print(f'{count=:<8,}{total=:<10,}{average=:,.2f}')
trees = [Tree('Cedar', 1_520), Tree('oak', 357), Tree('phoebe', 1_806)]
try:
for tree in trees:
# 改用「類別.屬性」存取。
show_count_and_average(count=Tree.count, total=Tree.total_age, average=Tree.average_age)
except Exception as e:
print(str(e))
@classmethod
裝飾器。類別屬性
,各位應該可以連想得到,必然也有類別方法
(class methods)。之前介紹的一般方法也可稱為實例方法(instance methods)。@classmethod
裝飾器。@classmethod
要放在@property
之前。Always use self for the first argument to instance methods.
Always use cls for the first argument to class methods.
cls
而不是大家熟悉的self
。註1: 筆者之前好像有說過,「方法」一詞過於「通用」,個人不大喜歡當作物件導向世界的專門術語,有時寧願用回「函數」。此句就是個例子。