1. 程式人生 > 實用技巧 >【Python】面向物件

【Python】面向物件

面向物件基礎

一. 理解面向物件

面向物件是一種抽象化的程式設計思想,很多程式語言中都有的一種思想。
例如:洗⾐服
思考:⼏種途徑可以完成洗⾐服?
答: 手洗 和 機洗。
手洗:找盆 - 放⽔ - 加洗⾐粉 - 浸泡 - 搓洗 - 擰⼲⽔ - 倒⽔ - 漂洗N次 - 擰⼲ - 晾晒。
機洗:開啟洗⾐機 - 放⾐服 - 加洗⾐粉 - 按下開始按鈕 - 晾晒。

思考:對比兩種洗⾐服途徑,同學們發現了什麼?
答:機洗更簡單
思考:機洗,只需要找到一臺洗⾐機,加入簡單操作就可以完成洗⾐服的工作,而不需要關心洗⾐機內部發生了什麼事情。

總結:面向物件就是將程式設計當成是一個事物,對外界來說,事物是直接使用的,不用去管他內部的情況。而程式設計就是設定事物能夠做什麼事。

二. 類和物件

思考:洗⾐機洗⾐服描述過程中,洗⾐機其實就是一個事物,即物件,洗⾐機物件哪來的呢?
答:洗⾐機是由工⼚工人制作出來。

思考:工⼚工人怎麼製作出的洗⾐機?
答:工人根據設計師設計的功能圖紙製作洗⾐機。

總結:圖紙 → 洗⾐機 → 洗⾐服。

在面向物件程式設計過程中,有兩個重要組成部分:類 和 物件。
類和物件的關係:用類去建立一個物件。

2.1 理解類和物件

2.1.1 類

類是對一系列具有相同特徵和行為的事物的統稱,是一個抽象的概念,不是真實存在的事物。

  • 特徵即是屬性
  • 行為即是方法

類比如是製造洗⾐機時要用到的圖紙,也就是說類是用來建立物件。

2.1.2 物件

物件是類創建出來的真實存在的事物,例如:洗⾐機。

注意:開發中,先有類,再有物件。

2.2 面向物件實現方法

2.2.1 定義類

Python2中類分為:經典類 和 新式類

語法

class 類名():
    程式碼
    ......

注意:類名要滿足識別符號命名規則,同時遵循大駝峰命名習慣。

體驗

class Washer():
    def wash(self):
        print('我會洗⾐服')

拓展:經典類
不由任意內建型別派生出的類,稱之為經典類

class 類名:
    程式碼
    ......

2.2.2 建立物件

物件又名例項。

語法

物件名 = 類名()

體驗

# 建立物件
haier1 = Washer()
# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)
# haier物件呼叫例項方法
haier1.wash()

注意:建立物件的過程也叫例項化物件。

2.2.3 self

self指的是呼叫該函式的物件。

# 1. 定義類
class Washer():
    def wash(self):
        print('我會洗⾐服')
        # <__main__.Washer object at 0x0000024BA2B34240>
        print(self)

# 2. 建立物件
haier1 = Washer()
# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)
# haier1物件呼叫例項方法
haier1.wash()

haier2 = Washer()
# <__main__.Washer object at 0x0000022005857EF0>
print(haier2)

注意:列印物件和self得到的結果是一致的,都是當前物件的記憶體中儲存地址。

三. 新增和獲取物件屬性

屬性即是特徵,比如:洗⾐機的寬度、高度、重量...
物件屬性既可以在類外面新增和獲取,也能在類裡面新增和獲取。

3.1 類外面新增物件屬性

語法

物件名.屬性名 = 值

體驗

haier1.width = 500
haier1.height = 800

3.2 類外面獲取物件屬性

語法

物件名.屬性名

體驗

print(f'haier1洗⾐機的寬度是{haier1.width}')
print(f'haier1洗⾐機的高度是{haier1.height}')

3.3 類裡面獲取物件屬性

語法

self.屬性名

體驗

# 定義類
class Washer():
    def print_info(self):
        # 類裡面獲取例項屬性
        print(f'haier1洗⾐機的寬度是{self.width}')
        print(f'haier1洗⾐機的高度是{self.height}')

# 建立物件
haier1 = Washer()

# 新增例項屬性
haier1.width = 500
haier1.height = 800

haier1.print_info()

四. 魔法方法

在Python中, xx() 的函式叫做魔法方法,指的是具有特殊功能的函式。

4.1 init() init()

4.1.1 體驗__init__() init()
思考:洗⾐機的寬度高度是與生俱來的屬性,可不可以在生產過程中就賦予這些屬性呢?
答:理應如此。

init() 方法的作用:初始化物件。

class Washer():
 
    # 定義初始化功能的函式
    def __init__(self):
        # 新增例項屬性
        self.width = 500
        self.height = 800
    
    def print_info(self):
        # 類裡面呼叫例項屬性
      print(f'洗⾐機的寬度是{self.width}, 高度是{self.height}')

haier1 = Washer()
haier1.print_info()

注意:

  • init() 方法,在建立一個物件時預設被呼叫,不需要手動呼叫
  • init(self) 中的self引數,不需要開發者傳遞,python直譯器會自動把當前的物件引用傳遞過去。

4.1.2 帶引數的__init__() init()
思考:一個類可以建立多個物件,如何對不同的物件設定不同的初始化屬性呢?
答:傳引數。

class Washer():
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def print_info(self):
        print(f'洗⾐機的寬度是{self.width}')
        print(f'洗⾐機的高度是{self.height}')

haier1 = Washer(10, 20)
haier1.print_info()

haier2 = Washer(30, 40)
haier2.print_info()

4.2 str()

當使用print輸出物件的時候,預設列印物件的記憶體地址。如果類定義了 str 方法,那麼就會列印從在這個方法中 return 的資料。

class Washer():
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def __str__(self):
        return '這是海爾洗⾐機的說明書'

haier1 = Washer(10, 20)
# 這是海爾洗⾐機的說明書
print(haier1)

4.3 del()

當刪除物件時,python直譯器也會預設呼叫 del() 方法。

class Washer():
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def __del__(self):
        print(f'{self}物件已經被刪除')

haier1 = Washer(10, 20)

# <__main__.Washer object at 0x0000026118223278>物件已經被刪除
del haier1

五. 綜合應用

5.1 烤地⽠

5.1.1 需求

需求主線:

  1. 被烤的時間和對應的地⽠狀態:
    0-3分鐘:生的
    3-5分鐘:半生不熟
    5-8分鐘:熟的
    超過8分鐘:烤糊了
  2. 新增的調料:
    使用者可以按自己的意願新增調料

5.1.2 步驟分析

需求涉及一個事物: 地⽠,故案例涉及一個類:地⽠類。

5.1.2.1 定義類
  • 地⽠的屬性
    • 被烤的時間
    • 地⽠的狀態
    • 新增的調料
  • 地⽠的方法
    • 被烤
      • 使用者根據意願設定每次烤地⽠的時間
      • 判斷地⽠被烤的總時間是在哪個區間,修改地⽠狀態
    • 新增調料
      • 使用者根據意願設定新增的調料
      • 將使用者新增的調料儲存
  • 顯示物件資訊
5.1.2.2 建立物件,呼叫相關例項方法

5.1.3 程式碼實現

5.1.3.1 定義類

地⽠屬性
定義地⽠初始化屬性,後期根據程式推進更新例項屬性

class SweetPotato():
    def __init__(self):
        # 被烤的時間
        self.cook_time = 0
        # 地⽠的狀態
        self.cook_static = '生的'
        # 調料列表
        self.condiments = []
5.1.3.2 定義烤地⽠方法
class SweetPotato():
    ......
    
    def cook(self, time):
        """烤地⽠的方法"""
        self.cook_time += time
        if 0 <= self.cook_time < 3:
            self.cook_static = '生的'
        elif 3 <= self.cook_time < 5:
            self.cook_static = '半生不熟'
        elif 5 <= self.cook_time < 8:
            self.cook_static = '熟了'
        elif self.cook_time >= 8:
            self.cook_static = '烤糊了'
5.1.3.3 書寫str魔法方法,用於輸出物件狀態
class SweetPotato():
    ......
    def __str__(self):
    return f'這個地⽠烤了{self.cook_time}分鐘, 狀態是{self.cook_static}'
5.1.3.4 建立物件,測試例項屬性和例項方法
digua1 = SweetPotato()
print(digua1)
digua1.cook(2)
print(digua1)
5.1.3.5 定義新增調料方法,並呼叫該例項方法
class SweetPotato():
    ......

    def add_condiments(self, condiment):
        """新增調料"""
        self.condiments.append(condiment)

    def __str__(self):
        return f'這個地⽠烤了{self.cook_time}分鐘, 狀態是{self.cook_static}, 新增的調料有{self.condiments}'
 
digua1 = SweetPotato()
print(digua1)

digua1.cook(2)
digua1.add_condiments('醬油')
print(digua1)

digua1.cook(2)
digua1.add_condiments('辣椒麵兒')
print(digua1)

digua1.cook(2)
print(digua1)

digua1.cook(2)
print(digua1)

5.1.4 程式碼總覽

# 定義類
class SweetPotato():
    def __init__(self):
        # 被烤的時間
        self.cook_time = 0
        # 地⽠的狀態
        self.cook_static = '生的'
        # 調料列表
        self.condiments = []

    def cook(self, time):
        """烤地⽠的方法"""
        self.cook_time += time
        if 0 <= self.cook_time < 3:
            self.cook_static = '生的'
        elif 3 <= self.cook_time < 5:
            self.cook_static = '半生不熟'
        elif 5 <= self.cook_time < 8:
            self.cook_static = '熟了'
        elif self.cook_time >= 8:
            self.cook_static = '烤糊了'

    def add_condiments(self, condiment):
        """新增調料"""
        self.condiments.append(condiment)

    def __str__(self):
        return f'這個地⽠烤了{self.cook_time}分鐘, 狀態是{self.cook_static}, 新增的調料有{self.condiments}'
        
digua1 = SweetPotato()
print(digua1)

digua1.cook(2)
digua1.add_condiments('醬油')
print(digua1)

digua1.cook(2)
digua1.add_condiments('辣椒麵兒')
print(digua1)

digua1.cook(2)
print(digua1)

digua1.cook(2)
print(digua1)

5.2 搬傢俱

5.2.1 需求

將小於房子剩餘面積的傢俱擺放到房子中

5.2.2 步驟分析

需求涉及兩個事物:房子 和 傢俱,故被案例涉及兩個類:房子類 和 傢俱類。

5.2.2.1 定義類
  • 房子類
    • 例項屬性
      • 房子地理位置
      • 房子佔地面積
      • 房子剩餘面積
      • 房子內傢俱列表
    • 例項方法
      • 容納傢俱
    • 顯示房屋資訊
  • 傢俱類
    • 傢俱名稱
    • 傢俱佔地面積
5.2.2.2 建立物件並呼叫相關方法

5.2.3 程式碼實現

5.2.3.1 定義類

傢俱類

class Furniture():
    def __init__(self, name, area):
        # 傢俱名字
        self.name = name
        # 傢俱佔地面積
        self.area = area

房子類

class Home():
    def __init__(self, address, area):
        # 地理位置
        self.address = address
        # 房屋面積
        self.area = area
        # 剩餘面積
        self.free_area = area
        # 傢俱列表
        self.furniture = []

    def __str__(self):
        return f'房子坐落於{self.address}, 佔地面積{self.area}, 剩餘面積{self.free_area}, 傢俱有{self.furniture}'

    def add_furniture(self, item):
        """容納傢俱"""
        if self.free_area >= item.area:
            self.furniture.append(item.name)
            # 傢俱搬入後,房屋剩餘面積 = 之前剩餘面積 - 該傢俱面積
            self.free_area -= item.area
        else:
            print('傢俱太大,剩餘面積不足,無法容納')
5.2.3.2 建立物件並呼叫例項屬性和方法
bed = Furniture('雙人床', 6)
jia1 = Home('北京', 1200)
print(jia1)

jia1.add_furniture(bed)
print(jia1)

sofa = Furniture('沙發', 10)
jia1.add_furniture(sofa)
print(jia1)

ball = Furniture('籃球場', 1500)
jia1.add_furniture(ball)
print(jia1)

面向物件-繼承

一. 繼承的概念

生活中的繼承,一般指的是子女繼承⽗輩的財產。

拓展1:經典類或舊式類
不由任意內建型別派生出的類,稱之為經典類。

class 類名:
    程式碼
    ......

拓展2:新式類

class 類名(object):
    程式碼

Python面向物件的繼承指的是多個類之間的所屬關係,即子類預設繼承⽗類的所有屬性和方法,具體如下:

# ⽗類A
class A(object):
    def __init__(self):
        self.num = 1
    def info_print(self):
        print(self.num)

# 子類B
class B(A):
    pass

result = B()
result.info_print() # 1

在Python中,所有類預設繼承object類,object類是頂級類或基類;其他子類叫做派生類。

二. 單繼承

故事主線:一個煎餅果子⽼師傅,在煎餅果子界摸爬滾打多年,研發了一套精湛的攤煎餅果子的技術。師⽗要把這套技術傳授給他的唯一的最得意的徒弟。

分析:徒弟是不是要繼承師⽗的所有技術?

# 1. 師⽗類
class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'
    
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')
 
# 2. 徒弟類
class Prentice(Master):
    pass

# 3. 建立物件daqiu
daqiu = Prentice()
# 4. 物件訪問例項屬性
print(daqiu.kongfu) 

# 5. 物件呼叫例項方法
daqiu.make_cake()

三. 多繼承

故事推進:daqiu是個愛學習的好孩子,想學習更多的煎餅果子技術,於是,在百度搜索到程式設計師,報班學習煎餅果子技術。

所謂多繼承意思就是一個類同時繼承了多個⽗類。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'

    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

# 建立學校類
class School(object):
    def __init__(self):
        self.kongfu = '[⿊⻢煎餅果子配方]'

    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class Prentice(School, Master):
   pass

daqiu = Prentice()
print(daqiu.kongfu)
daqiu.make_cake()

注意:當一個類有多個⽗類的時候,預設使用第一個⽗類的同名屬性和方法。

四. 子類重寫⽗類同名方法和屬性

故事:daqiu掌握了師⽗和培訓的技術後,自己潛心鑽研出自己的獨門配方的一套全新的煎餅果子技術。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'
    
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class School(object):
    def __init__(self):
        self.kongfu = '[⿊⻢煎餅果子配方]'
    
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

# 獨創配方
class Prentice(School, Master):
     def __init__(self):
        self.kongfu = '[獨創煎餅果子配方]'
 
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

daqiu = Prentice()
print(daqiu.kongfu)
daqiu.make_cake()

print(Prentice.__mro__)

子類和⽗類具有同名屬性和方法,預設使用子類的同名屬性和方法。

五. 子類呼叫⽗類的同名方法和屬性

故事:很多顧客都希望也能吃到古法和⿊⻢的技術的煎餅果子。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'
    
    def make_cake(self):
    print(f'運用{self.kongfu}製作煎餅果子')

class School(object):
    def __init__(self):
        self.kongfu = '[⿊⻢煎餅果子配方]'
    
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[獨創煎餅果子配方]'
    
    def make_cake(self):
        # 如果是先呼叫了⽗類的屬性和方法,⽗類屬性會覆蓋子類屬性,故在呼叫屬性前,先呼叫自己子類的初始化
        self.__init__()
        print(f'運用{self.kongfu}製作煎餅果子')
 
    # 呼叫⽗類方法,但是為保證呼叫到的也是⽗類的屬性,必須在呼叫方法前呼叫⽗類的初始化
    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)
 
    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)

daqiu = Prentice()

daqiu.make_cake()

daqiu.make_master_cake()

daqiu.make_school_cake()

daqiu.make_cake()

六. 多層繼承

故事:N年後,daqiu⽼了,想要把所有技術傳承給自己的徒弟。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'
    
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class School(object):
    def __init__(self):
        self.kongfu = '[⿊⻢煎餅果子配方]'
    
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[獨創煎餅果子配方]'
 
    def make_cake(self):
        self.__init__()
        print(f'運用{self.kongfu}製作煎餅果子')
 
    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)
 
    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)

# 徒孫類
class Tusun(Prentice):
    pass

xiaoqiu = Tusun()

xiaoqiu.make_cake()

xiaoqiu.make_school_cake()

xiaoqiu.make_master_cake()

七. super()呼叫⽗類方法

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'
    
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class School(Master):
    def __init__(self):
        self.kongfu = '[⿊⻢煎餅果子配方]'
 
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')
        # 方法2.1
        # super(School, self).__init__()
        # super(School, self).make_cake()
        
        # 方法2.2
        super().__init__()
        super().make_cake()

class Prentice(School):
    def __init__(self):
        self.kongfu = '[獨創煎餅果子技術]'
    
    def make_cake(self):
        self.__init__()
        print(f'運用{self.kongfu}製作煎餅果子')
 
    # 子類呼叫⽗類的同名方法和屬性:把⽗類的同名屬性和方法再次封裝
    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)
 
    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)
 
    # 一次性呼叫⽗類的同名屬性和方法
    def make_old_cake(self):
        # 方法一:程式碼冗餘;⽗類類名如果變化,這裡程式碼需要頻繁修改
        # Master.__init__(self)
        # Master.make_cake(self)
        # School.__init__(self)
        # School.make_cake(self)
        # 方法二: super()
        # 方法2.1 super(當前類名, self).函式()
        # super(Prentice, self).__init__()
        # super(Prentice, self).make_cake()
        # 方法2.2 super().函式()
        super().__init__()
        super().make_cake()

daqiu = Prentice()

daqiu.make_old_cake()

注意:使用super() 可以自動查詢⽗類。呼叫順序遵循 mro 類屬性的順序。比較適合單繼承使用。

八. 私有許可權

8.1 定義私有屬性和方法

在Python中,可以為例項屬性和方法設定私有許可權,即設定某個例項屬性或例項方法不繼承給子類。

故事:daqiu把技術傳承給徒弟的同時,不想把自己的錢(2000000個億)繼承給徒弟,這個時候就要為 錢 這個例項屬性設定私有許可權。

設定私有許可權的方法:在屬性名和方法名 前面 加上兩個下劃線 __。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'
 
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class School(object):
    def __init__(self):
        self.kongfu = '[⿊⻢煎餅果子配方]'
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[獨創煎餅果子配方]'
        # 定義私有屬性
        self.__money = 2000000
    
    # 定義私有方法
    def __info_print(self):
        print(self.kongfu)
        print(self.__money)
 
    def make_cake(self):
        self.__init__()
        print(f'運用{self.kongfu}製作煎餅果子')
 
    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)
 
    def make_school_cake(self):
    School.__init__(self)
    School.make_cake(self)

# 徒孫類
class Tusun(Prentice):
    pass

daqiu = Prentice()
# 物件不能訪問私有屬性和私有方法
# print(daqiu.__money)
# daqiu.__info_print()

xiaoqiu = Tusun()
# 子類無法繼承⽗類的私有屬性和私有方法
# print(xiaoqiu.__money) # 無法訪問例項屬性__money
# xiaoqiu.__info_print()

注意:私有屬性和私有方法只能在類裡面訪問和修改。

8.2 獲取和修改私有屬性值

在Python中,一般定義函式名 get_xx 用來獲取私有屬性,定義 set_xx 用來修改私有屬性值。

class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'
    
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class School(object): 
    def __init__(self):
        self.kongfu = '[⿊⻢煎餅果子配方]'
 
    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')

class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[獨創煎餅果子配方]'
        self.__money = 2000000
    
    # 獲取私有屬性
    def get_money(self):
        return self.__money
    
    # 修改私有屬性
    def set_money(self):
        self.__money = 500
 
    def __info_print(self):
        print(self.kongfu)
        print(self.__money)
    
    def make_cake(self):
        self.__init__()
        print(f'運用{self.kongfu}製作煎餅果子')

    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)
    
    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)
    
# 徒孫類
class Tusun(Prentice):
    pass

daqiu = Prentice()

xiaoqiu = Tusun()
# 呼叫get_money函式獲取私有屬性money的值
print(xiaoqiu.get_money())
# 呼叫set_money函式修改私有屬性money的值
xiaoqiu.set_money()
print(xiaoqiu.get_money())

面向物件-其他

一. 面向物件三大特性

  • 封裝
    • 將屬性和方法書寫到類的裡面的操作即為封裝
    • 封裝可以為屬性和方法新增私有許可權
  • 繼承
    • 子類預設繼承⽗類的所有屬性和方法
    • 子類可以重寫⽗類屬性和方法
  • 多型
    • 傳入不同的物件,產生不同的結果

二. 多型

2.1 瞭解多型

多型指的是一類事物有多種形態,(一個抽象類有多個子類,因而多型的概念依賴於繼承)。

  • 定義:多型是一種使用物件的方式,子類重寫⽗類方法,呼叫不同子類物件的相同⽗類方法,可以產生不同的執行結果
  • 好處:呼叫靈活,有了多型,更容易編寫出通用的程式碼,做出通用的程式設計,以適應需求的不斷變化!
  • 實現步驟:
    • 定義⽗類,並提供公共方法
    • 定義子類,並重寫⽗類方法
    • 傳遞子類物件給呼叫者,可以看到不同子類執行效果不同

2.2 體驗多型

class Dog(object):
    def work(self): # ⽗類提供統一的方法,哪怕是空方法
        print('指哪打哪...')

class ArmyDog(Dog): # 繼承Dog類
    def work(self): # 子類重寫⽗類同名方法
        print('追擊敵人...')

class DrugDog(Dog):
    def work(self):
        print('追查毒品...')

class Person(object):
    def work_with_dog(self, dog): # 傳入不同的物件,執行不同的程式碼,即不同的work函式
        dog.work()

ad = ArmyDog()
dd = DrugDog()

daqiu = Person()
daqiu.work_with_dog(ad)
daqiu.work_with_dog(dd)

三. 類屬性和例項屬性

3.1 類屬性

3.1.1 設定和訪問類屬性

  • 類屬性就是 類物件 所擁有的屬性,它被 該類的所有例項物件 所共有。
  • 類屬性可以使用 類物件 或 例項物件 訪問。
class Dog(object):
    tooth = 10

wangcai = Dog()
xiaohei = Dog()

print(Dog.tooth) # 10
print(wangcai.tooth) # 10
print(xiaohei.tooth) # 10

類屬性的優點

  • 類的例項 記錄的某項資料 始終保持一致時,則定義類屬性。
  • 例項屬性 要求 每個物件 為其 單獨開闢一份記憶體空間 來記錄資料,而 類屬性 為全類所共有,僅佔用一份記憶體,更加節省記憶體空間。

3.1.2 修改類屬性

類屬性只能通過類物件修改,不能通過例項物件修改,如果通過例項物件修改類屬性,表示的是建立了一個例項屬性。

class Dog(object):
    tooth = 10

wangcai = Dog()
xiaohei = Dog()

# 修改類屬性
Dog.tooth = 12
print(Dog.tooth) # 12
print(wangcai.tooth) # 12
print(xiaohei.tooth) # 12

# 不能通過物件修改屬性,如果這樣操作,實則是建立了一個例項屬性
wangcai.tooth = 20
print(Dog.tooth) # 12
print(wangcai.tooth) # 20
print(xiaohei.tooth) # 12

3.2 例項屬性

class Dog(object):
    def __init__(self):
        self.age = 5
    def info_print(self):
        print(self.age)

wangcai = Dog()
print(wangcai.age) # 5
# print(Dog.age) # 報錯:例項屬性不能通過類訪問
wangcai.info_print() # 5

四. 類方法和靜態方法

4.1 類方法

4.1.1 類方法特點

  • 第一個形參是類物件的方法
  • 需要用裝飾器 @classmethod 來標識其為類方法,對於類方法,第一個引數必須是類物件,一般以 cls 作為第一個引數。

4.1.2 類方法使用場景

  • 當方法中 需要使用類物件 (如訪問私有類屬性等)時,定義類方法
  • 類方法一般和類屬性配合使用
class Dog(object):
    __tooth = 10
    
    @classmethod
    def get_tooth(cls):
        return cls.__tooth

wangcai = Dog()
result = wangcai.get_tooth()
print(result) # 10

4.2 靜態方法

4.2.1 靜態方法特點

  • 需要通過裝飾器 @staticmethod 來進行修飾,靜態方法既不需要傳遞類物件也不需要傳遞例項物件(形參沒有self/cls)。
  • 靜態方法 也能夠通過 例項物件 和 類物件 去訪問。

4.2.2 靜態方法使用場景

  • 當方法中 既不需要使用例項物件(如例項物件,例項屬性),也不需要使用類物件 (如類屬性、類方法、建立例項等)時,定義靜態方法
  • 取消不需要的引數傳遞,有利於 減少不必要的記憶體佔用和效能消耗
class Dog(object):
    @staticmethod
    def info_print():
        print('這是一個狗類,用於建立狗例項....')

wangcai = Dog()
# 靜態方法既可以使用物件訪問又可以使用類訪問
wangcai.info_print()
Dog.info_print()