面向物件-類屬性和類方法,靜態方法
阿新 • • 發佈:2018-12-23
類屬性和類方法
目標
- 類的結構
- 類屬性和例項屬性
- 類方法和靜態方法
01. 類的結構
1.1 術語 —— 例項
- 使用面相物件開發,第 1 步 是設計 類
- 使用 類名() 建立物件,建立物件 的動作有兩步:
- 1) 在記憶體中為物件 分配空間
- 2) 呼叫初始化方法
__init__
為 物件初始化
- 物件建立後,記憶體 中就有了一個物件的 實實在在 的存在 —— 例項
因此,通常也會把:
- 創建出來的 物件 叫做 類 的 例項
- 建立物件的 動作 叫做 例項化
- 物件的屬性 叫做 例項屬性
- 物件呼叫的方法 叫做 例項方法
在程式執行時:
- 物件各自擁有自己的 例項屬性
- 呼叫物件方法,可以通過
self.
- 訪問自己的屬性
- 呼叫自己的方法
結論
- 每一個物件 都有自己 獨立的記憶體空間,儲存各自不同的屬性
- 多個物件的方法,在記憶體中只有一份,在呼叫方法時,需要把物件的引用 傳遞到方法內部
1.2 類是一個特殊的物件
Python
中 一切皆物件:
class AAA:
定義的類屬於 類物件obj1 = AAA()
屬於 例項物件
- 在程式執行時,類 同樣 會被載入到記憶體
- 在
Python
中,類 是一個特殊的物件 —— 類物件 - 在程式執行時,類物件 在記憶體中 只有一份
- 除了封裝 例項 的 屬性 和 方法外,類物件 還可以擁有自己的 屬性 和 方法
- 類屬性
- 類方法
- 通過 類名. 的方式可以 訪問類的屬性 或者 呼叫類的方法
02. 類屬性和例項屬性
2.1 概念和使用
- 類屬性 就是給 類物件 中定義的 屬性
- 通常用來記錄 與這個類相關 的特徵
- 類屬性 不會用於記錄 具體物件的特徵
示例需求
- 定義一個 工具類
- 每件工具都有自己的
name
- 需求 —— 知道使用這個類,建立了多少個工具物件?
class Tool(object):
# 使用賦值語句,定義類屬性,記錄建立工具物件的總數
count = 0
def __init__(self, name):
self.name = name
# 針對類屬性做一個計數+1
Tool.count += 1
# 建立工具物件
tool1 = Tool("斧頭")
tool2 = Tool("榔頭")
tool3 = Tool("鐵鍬")
# 知道使用 Tool 類到底建立了多少個物件?
print("現在建立了 %d 個工具" % Tool.count)
2.2 屬性的獲取機制(科普)
- 在
Python
中 屬性的獲取 存在一個 向上查詢機制
- 因此,要訪問類屬性有兩種方式:
- 類名.類屬性
- 物件.類屬性 (不推薦)
注意
- 如果使用
物件.類屬性 = 值
賦值語句,只會 給物件新增一個屬性,而不會影響到 類屬性的值
03. 類方法和靜態方法
3.1 類方法
- 類屬性 就是針對 類物件 定義的屬性
- 使用 賦值語句 在
class
關鍵字下方可以定義 類屬性 - 類屬性 用於記錄 與這個類相關 的特徵
- 使用 賦值語句 在
- 類方法 就是針對 類物件 定義的方法
- 在 類方法 內部可以直接訪問 類屬性 或者呼叫其他的 類方法
語法如下
@classmethod
def 類方法名(cls):
pass
- 類方法需要用 修飾器
@classmethod
來標識,告訴直譯器這是一個類方法 - 類方法的 第一個引數 應該是
cls
- 由 哪一個類 呼叫的方法,方法內的
cls
就是 哪一個類的引用 - 這個引數和 例項方法 的第一個引數是
self
類似 - 提示 使用其他名稱也可以,不過習慣使用
cls
- 由 哪一個類 呼叫的方法,方法內的
- 通過 類名. 呼叫 類方法,呼叫方法時,不需要傳遞
cls
引數 - 在方法內部
- 可以通過
cls.
訪問類的屬性 - 也可以通過
cls.
呼叫其他的類方法
- 可以通過
示例需求
- 定義一個 工具類
- 每件工具都有自己的
name
- 需求 —— 在 類 封裝一個
show_tool_count
的類方法,輸出使用當前這個類,建立的物件個數
@classmethod
def show_tool_count(cls):
"""顯示工具物件的總數"""
print("工具物件的總數 %d" % cls.count)
在類方法內部,可以直接使用
cls
訪問 類屬性 或者 呼叫類方法
3.2 靜態方法
-
在開發時,如果需要在 類 中封裝一個方法,這個方法:
- 既 不需要 訪問 例項屬性 或者呼叫 例項方法
- 也 不需要 訪問 類屬性 或者呼叫 類方法
-
這個時候,可以把這個方法封裝成一個 靜態方法
語法如下
@staticmethod
def 靜態方法名():
pass
- 靜態方法 需要用 修飾器
@staticmethod
來標識,告訴直譯器這是一個靜態方法 - 通過 類名. 呼叫 靜態方法
class Dog(object):
# 狗物件計數
dog_count = 0
@staticmethod
def run():
# 不需要訪問例項屬性也不需要訪問類屬性的方法
print("狗在跑...")
def __init__(self, name):
self.name = name
3.3 方法綜合案例
需求
- 設計一個
Game
類 - 屬性:
- 定義一個 類屬性
top_score
記錄遊戲的 歷史最高分 - 定義一個 例項屬性
player_name
記錄 當前遊戲的玩家姓名
- 定義一個 類屬性
- 方法:
- 靜態方法
show_help
顯示遊戲幫助資訊 - 類方法
show_top_score
顯示歷史最高分 - 例項方法
start_game
開始當前玩家的遊戲
- 靜態方法
- 主程式步驟
- 1) 檢視幫助資訊
- 2) 檢視歷史最高分
- 3) 建立遊戲物件,開始遊戲
案例小結
- 例項方法 —— 方法內部需要訪問 例項屬性
- 例項方法 內部可以使用 類名. 訪問類屬性
- 類方法 —— 方法內部 只 需要訪問 類屬性
- 靜態方法 —— 方法內部,不需要訪問 例項屬性 和 類屬性
提問
如果方法內部 即需要訪問 例項屬性,又需要訪問 類屬性,應該定義成什麼方法?
答案
- 應該定義 例項方法
- 因為,類只有一個,在 例項方法 內部可以使用 類名. 訪問類屬性
class Game(object):
# 遊戲最高分,類屬性
top_score = 0
@staticmethod
def show_help():
print("幫助資訊:讓殭屍走進房間")
@classmethod
def show_top_score(cls):
print("遊戲最高分是 %d" % cls.top_score)
def __init__(self, player_name):
self.player_name = player_name
def start_game(self):
print("[%s] 開始遊戲..." % self.player_name)
# 使用類名.修改歷史最高分
Game.top_score = 999
# 1. 檢視遊戲幫助
Game.show_help()
# 2. 檢視遊戲最高分
Game.show_top_score()
# 3. 建立遊戲物件,開始遊戲
game = Game("小明")
game.start_game()
# 4. 遊戲結束,檢視遊戲最高分
Game.show_top_score()
再論靜態方法和類方法
1. 類屬性、例項屬性
它們在定義和使用中有所區別,而最本質的區別是記憶體中儲存的位置不同,
- 例項屬性屬於物件
- 類屬性屬於類
class Province(object):
# 類屬性
country = '中國'
def __init__(self, name):
# 例項屬性
self.name = name
# 建立一個例項物件
obj = Province('山東省')
# 直接訪問例項屬性
print(obj.name)
# 直接訪問類屬性
Province.country
由上述程式碼可以看出【例項屬性需要通過物件來訪問】【類屬性通過類訪問】,在使用上可以看出例項屬性和類屬性的歸屬是不同的。
其在內容的儲存方式類似如下圖:
由上圖看出:
- 類屬性在記憶體中只儲存一份
- 例項屬性在每個物件中都要儲存一份
應用場景:
- 通過類建立例項物件時,如果每個物件需要具有相同名字的屬性,那麼就使用類屬性,用一份既可
2. 例項方法、靜態方法和類方法
方法包括:例項方法、靜態方法和類方法,三種方法在記憶體中都歸屬於類,區別在於呼叫方式不同。
- 例項方法:由物件呼叫;至少一個self引數;執行例項方法時,自動將呼叫該方法的物件賦值給self;
- 類方法:由類呼叫; 至少一個cls引數;執行類方法時,自動將呼叫該方法的類賦值給cls;
- 靜態方法:由類呼叫;無預設引數;
class Foo(object):
def __init__(self, name):
self.name = name
def ord_func(self):
""" 定義例項方法,至少有一個self引數 """
# print(self.name)
print('例項方法')
@classmethod
def class_func(cls):
""" 定義類方法,至少有一個cls引數 """
print('類方法')
@staticmethod
def static_func():
""" 定義靜態方法 ,無預設引數"""
print('靜態方法')
f = Foo("中國")
# 呼叫例項方法
f.ord_func()
# 呼叫類方法
Foo.class_func()
# 呼叫靜態方法
Foo.static_func()
對比
- 相同點:對於所有的方法而言,均屬於類,所以 在記憶體中也只儲存一份
- 不同點:方法呼叫者不同、呼叫方法時自動傳入的引數不同。