1. 程式人生 > >面向物件-類屬性和類方法,靜態方法

面向物件-類屬性和類方法,靜態方法

類屬性和類方法

目標

  • 類的結構
  • 類屬性和例項屬性
  • 類方法和靜態方法

01. 類的結構

1.1 術語 —— 例項

  1. 使用面相物件開發,第 1 步 是設計 
  2. 使用 類名() 建立物件,建立物件 的動作有兩步:
    • 1) 在記憶體中為物件 分配空間
    • 2) 呼叫初始化方法 __init__ 為 物件初始化
  3. 物件建立後,記憶體 中就有了一個物件的 實實在在 的存在 —— 例項

因此,通常也會把:

  1. 創建出來的 物件 叫做  的 例項
  2. 建立物件的 動作 叫做 例項化
  3. 物件的屬性 叫做 例項屬性
  4. 物件呼叫的方法 叫做 例項方法

在程式執行時:

  1. 物件各自擁有自己的 例項屬性
  2. 呼叫物件方法,可以通過 self.
    • 訪問自己的屬性
    • 呼叫自己的方法

結論

  • 每一個物件 都有自己 獨立的記憶體空間儲存各自不同的屬性
  • 多個物件的方法在記憶體中只有一份,在呼叫方法時,需要把物件的引用 傳遞到方法內部

1.2 類是一個特殊的物件

Python 中 一切皆物件

  • class AAA: 定義的類屬於 類物件
  • obj1 = AAA() 屬於 例項物件
  • 在程式執行時, 同樣 會被載入到記憶體
  • 在 Python 中, 是一個特殊的物件 —— 類物件
  • 在程式執行時,類物件 在記憶體中 只有一份
    ,使用 一個類 可以創建出 很多個物件例項
  • 除了封裝 例項 的 屬性 和 方法外,類物件 還可以擁有自己的 屬性 和 方法
    1. 類屬性
    2. 類方法
  • 通過 類名. 的方式可以 訪問類的屬性 或者 呼叫類的方法

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 中 屬性的獲取 存在一個 向上查詢機制

  • 因此,要訪問類屬性有兩種方式:
    1. 類名.類屬性
    2. 物件.類屬性 (不推薦)

注意

  • 如果使用 物件.類屬性 = 值 賦值語句,只會 給物件新增一個屬性,而不會影響到 類屬性的值

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 方法綜合案例

需求

  1. 設計一個 Game 類
  2. 屬性:
    • 定義一個 類屬性 top_score 記錄遊戲的 歷史最高分
    • 定義一個 例項屬性 player_name 記錄 當前遊戲的玩家姓名
  3. 方法:
    • 靜態方法 show_help 顯示遊戲幫助資訊
    • 類方法 show_top_score 顯示歷史最高分
    • 例項方法 start_game 開始當前玩家的遊戲
  4. 主程式步驟
    • 1) 檢視幫助資訊
    • 2) 檢視歷史最高分
    • 3) 建立遊戲物件,開始遊戲

案例小結

  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()

 

對比

  • 相同點:對於所有的方法而言,均屬於類,所以 在記憶體中也只儲存一份
  • 不同點:方法呼叫者不同、呼叫方法時自動傳入的引數不同。