Python基礎語法(七:類與物件)

販賣溫柔的神明發表於2020-12-19

不在壓力與疲憊中沉浮,
不在良辰與美景中迷失。
不在奮鬥與競爭中麻木,
不在輝煌與騰飛中失足。


一、Python中的類與物件

1.類與物件的概念

物件物件導向程式設計的 兩個 核心概念

物以類聚,人以群分
人類
鳥類
植物類
超人類
.....
劃分類的標準是?擁有共同的特性?動作?

類 英文單詞: class
他們有相似點。共同之處

  • 是對一群具有 相同特徵 或者 行為 的事物的一個統稱,是抽象的,不能直接使用
    • 特徵 被稱為 屬性
      • 它是什麼樣的
什麼是屬性???
表示的是類/物件的特徵。

特徵是別人不具備的。

類屬性(類變數):
這個類的特徵,這個群體的特徵, 別的類(別的群體)可能不具備
例項屬性(例項變數) :
這個個體的特徵,類當中的其他成員可能不具備。


類屬性的獲取:
類名.屬性

類屬性:可以先天定義也可以  後天學習(手工新增)

class Man:
    # 定義類屬性
    gender = '男'
    power = "強"
    handsome = 'very very'


print(Man.power)  # Man類的power屬性
print(Man.gender) # Man類的gender屬性

# 後天學習(手工新增的)
Man.hello = 'hello world'
print(Man.hello)

# 例項屬性的後天學習獲取
yuz = Man()
# yuz.handsome = 'very very'
print(yuz.handsome)

kaishi = Man()
kaishi.handsome = '一丟丟'
print(kaishi.handsome)

# 類屬性和例項屬性:

# 類屬性:所有的成員都是一樣的。
# 例項屬性:不是每個成員都一樣


# 第二個區別:
#類屬性,可以被例項、物件訪問
# 例項屬性, 不可以被類獲取
print(Man.eye)

class Man:
    # 定義類屬性
    gender = '男'
    power = "強"
    handsome = 'very very'


yuz = Man()
kaishi = Man()

print(yuz.gender)
print(kaishi.gender)

yuz.eye = '藍色'
# AttributeError, 屬性錯誤。
# print(kaishi.eye)
print(Man.eye)

  **行為** 被稱為 **方法**
      - 它可以做什麼
方法:表示類 、 物件的行為。
方法本質上是函式。

屬性名稱:名詞。
方法:動詞,

方法 vS 函式:
1, self
2, 放在類裡面,縮排的。
3, 呼叫過程不一樣。 方法需要加字首,類名或者物件名。
函式前面要麼不加,要麼加的模組名。

# 物件.方法()
Man().drink()

# 例項方法,物件方法:
1, 第一個引數名稱,規定是 self,
2,  例項方法在呼叫的時候,字首是物件,不能是類

# 類方法:
1, 在方法的上面新增 @classmethod
2, 把例項方法當中的 self 改成 cls,

# 靜態方法
靜態方法表示: 方法的上面加上一個 @staticmethod
不需要用 self, cls 作為固定引數

是剛剛好放在一個類當中的普通函式而已。
除了放在類當中,和普通函式沒什麼區別。

沒有實際作用,和類或者物件沒有關係(聯絡),
為什麼要用靜態方法、為什麼要把普通的函式放到類當中去??
方便管理

呼叫靜態方法。
靜態方法,(普通函式) 只需要在呼叫靜態方法,前面加上類名或者物件名

類外面的普通函式:普通函式不需要加類,模組



實際使用當中:
例項方法: 98% , 例項方法佔大多數情況。
不知道用什麼方法,就用例項方法。

類方法:後面會有特殊情況

靜態方法: 可以用普通函式代替,不是必須的。(管理方便。)
 """



class Man:
    # 定義類屬性
    gender = '男'
    power = "強"
    handsome = 'very very'

    @staticmethod
    def tianqiyubao():
        print("天氣預報:今天是晴天。")

    @classmethod
    def eat(cls):
        print("正在吃飯")
        return "hello world"

    def drink(self):
        """喝。。。"""
        pass

    def play_game(self):
        """玩遊戲"""
        pass

# # 物件.方法()
# Man().drink()
#
# yuz = Man()
# yuz.drink()

# 行為是屬於某一個人的。不能被類呼叫的
#  不 OK,  Man.drink()

# 列印函式的呼叫列印出來的是函式的返回值。
# def run():
#     pass
#
# print(run())


# print(Man.eat())
# print(Man().eat())


# 靜態方法的使用
Man.tianqiyubao()
Man().tianqiyubao()

  • 就相當於建造房子時的施工圖紙(blueprint),是一個模板,是負責建立**房子(物件)**的

物件 object: 東西
什麼是物件 object
又被稱為 例項 instance

  • 物件由類建立出來的一個具體存在,可以直接使用
  • 哪一個類 建立出來的 物件,就擁有在 哪一個類 中定義的:
    • 屬性
    • 方法
  • 物件 就相當於用 施工圖紙 建造 的房子

在程式開發中,應該 先有類,再有物件

2. 類與物件的關係

  • 類是模板物件 是根據 這個模板建立出來的
  • 先有類,再有物件
  • 只有一個,而 物件 可以有很多個
    • 不同的物件 之間 屬性 可能會各不相同
  • 中定義了什麼 屬性和方法物件 中就有什麼屬性和方法,不可能多,也不可能少

3.類的設計

在使用面相物件開發前,應該首先分析需求,確定一下,程式中需要包含哪些類!

類的設計要素

在程式開發中,要設計一個類,通常需要滿足一下三個要素:

  • 類名
    • 滿足大駝峰命名法
  • 屬性
    • 這類事物具有什麼樣的特徵
  • 方法
    • 這類事物具有什麼樣的行為

大駝峰命名法

  • 每一個單詞的首字母大寫

  • 單詞與單詞之間沒有下劃線

確定類名

名詞提煉法 分析 整個業務流程,出現的 名詞,通常就是找到的類

確定屬性和方法

  • 物件的特徵描述,通常可以定義成 屬性
  • 物件具有的行為(動詞),通常可以定義成方法
類的定義:
class 類名:
    類的組成部分

類的呼叫:
print(類名)
變數 = 類名
<class '__main__.Man'>
<__main__.Man object at 0x0000022ABEE74670>
看到有尖括號的,一般來說就是一個類 / 物件。

4. 定義類

  • Python 中要定義一個只包含方法的類,語法格式如下:
class 類名:

    def 方法1(self, 引數列表):
        pass
    
    def 方法2(self, 引數列表):
        pass
  • 方法 的定義格式和之前學習過的函式 幾乎一樣
  • 區別在於第一個引數必須是 self

建立物件

  • 當一個類定義完成之後,要使用這個類來建立物件,語法格式如下:
物件變數 = 類名()

self

使用 self 在方法內部輸出每個人的名字

哪一個物件 呼叫的方法,方法內的 self 就是 哪一個物件的便利貼

  • 在類封裝的方法內部,self 就表示 當前呼叫方法的物件自身

  • 呼叫方法時,程式設計師不需要傳遞 self 引數

  • 在方法內部

    • 可以通過 self. 訪問物件的屬性
    • 也可以通過 self. 呼叫其他的物件方法
  • Python 中要定義帶屬性的類,語法格式如下:

初始化方法

  • 當使用 類名() 建立物件時,會 自動 執行以下操作:

    • 為物件在記憶體中 分配空間 —— 建立物件

    • 為物件的屬性 設定初始值 —— 初始化方法(init)

  • 這個 初始化方法 就是 __init__ 方法,__init__ 是物件的內建方法

__init__ 方法是 專門 用來定義一個類 具有哪些屬性的方法

在初始化方法內部定義屬性

  • __init__ 方法內部使用 self.屬性名 = 屬性的初始值 就可以 定義屬性
  • 定義屬性之後,再使用 Person 類建立的物件,都會擁有該屬性

"""
什麼是初始化??
要通過定義的類得到一個具體的物件。  生出來,造出來。

物件個體,初始化過程保證生出來是不一樣的。
我們自己給東西進行出廠設定。

特定的方法中去控制:
__init__

例項屬性的定義2:
1, 類外面:  物件名.屬性
2, 類裡面:  self.屬性


__init__ 定義的形式引數 和  物件的例項化 a = Man() 的實際引數,是一一對應的。
1. 必須 return None
2. 傳入的引數必須要設定成例項屬性,才能被被物件訪問到。


self: 在 **類裡面** ,表示一個 **物件**   他自己。自我, this
cls, : 在 **類裡面** ,表示一個 **類**   他自己。自我, this


例項方法當中,可不可以去定義例項屬性。??

"""

# def run():
#     pass

class Man:
    # 定義類屬性
    gender = '男'
    power = "強"
    handsome = 'very very'

    def __init__(self,  name, face_shape='圓臉'):
        """物件的初始化過程。 讓物件之間長得不太一樣。"""
        # 定義, 在類裡面,定義例項屬性使用 self.屬性 = ...
        self.face = face_shape
        self.name = name


    @staticmethod
    def tianqiyubao():
        print("天氣預報:今天是晴天。")

    @classmethod
    def eat(cls):
        print("正在吃飯")
        print(cls.power)
        return "hello world"

    def drink(self, brand):
        """喝。。。"""
        print("{} 正在喝 {} 酒".format(self.name, brand))
        # self.play_game()
        self.eat()
        # 定義例項屬性
        self.single = False

    def play_game(self):
        """玩遊戲"""
        print("玩遊戲")



yuz = Man('yuz', '方臉')

# print(yuz.face)
# print(yuz.name)
# yuz.drink('茅臺')

# 整容的權利,可以後天改變屬性
# yuz.face = "帥臉"
# print(yuz.face)


# 初始化以後 。 single 屬性是後天定義的,
yuz.drink('五糧液')
print(yuz.single)
yuz.single = True
print(yuz.single)



# print(a)

5. __str__ 方法

  • Python 中,使用 print 輸出 物件變數,預設情況下,會輸出這個變數 引用的物件由哪一個類建立的物件,以及 在記憶體中的地址十六進位制表示
  • 如果在開發中,希望使用 print 輸出 物件變數 時,能夠列印 自定義的內容,就可以利用 __str__ 這個內建方法了

注意:__str__ 方法必須返回一個字串

二、身份運算子

身份運算子用於 比較 兩個物件的 記憶體地址 是否一致 —— 是否是對同一個物件的引用

  • Python 中針對 None 比較時,建議使用 is 判斷
運算子描述例項
isis 是判斷兩個識別符號是不是引用同一個物件x is y,類似 id(x) == id(y)
is notis not 是判斷兩個識別符號是不是引用不同物件x is not y,類似 id(a) != id(b)

is 與 == 區別

is 用於判斷 兩個變數 引用物件是否為同一個
== 用於判斷 引用變數的值 是否相等

三、 類屬性和例項屬性

1.概念

  • 類屬性 就是給 定義的 屬性
  • 通常用來記錄 與這個類相關 的特徵
  • 類屬性 不會記錄具體物件的特徵

  動態屬性設定
class Phone:

    def __init__(self, number):
        self.number = number


phone = Phone('137')

# # 物件.屬性
# print(phone.number)
#
# # 動態獲取屬性
# print(getattr(phone, 'number'))

# 動態獲取不存在的屬性
# print(phone.brand)
# 可以加上 default 引數,設定預設值。當沒有該屬性的屬性,不會報錯。
# 而是返回預設值。
# print(getattr(phone, 'brand', default='蘋果'))

# 動態屬性,屬性名 == 變數

prop_name = 'brand'
# phone.prop_name
# phone.brand
print(getattr(phone, prop_name, '蘋果'))
print(phone.brand)

# 設定 set 屬性。
# print(phone.number)
# setattr(phone, 'number', '155')
# # phone.number = 155
# phone.color = '白色'
# print(phone.number)

四、類方法和靜態方法

1.類方法

  • 類屬性 就是針對 定義的屬性
    • 使用 賦值語句class 關鍵字下方可以定義 類屬性
    • 類屬性 用於記錄 與這個類相關 的特徵
  • 類方法 就是針對 定義的方法
    • 類方法 內部可以直接訪問 類屬性 或者呼叫其他的 類方法

語法如下

@classmethod
def 類方法名(cls):
    pass
  • 類方法需要用 修飾器 @classmethod 來標識,告訴直譯器這是一個類方法

  • 類方法的 第一個引數 應該是 cls

    • 哪一個類 呼叫的方法,方法內的 cls 就是 哪一個類的引用
    • 這個引數和 例項方法 的第一個引數是 self 類似
    • 提示 使用其他名稱也可以,不過習慣使用 cls
  • 通過 類名. 呼叫 類方法呼叫方法時,不需要傳遞 cls 引數

  • 在方法內部

  • 可以通過 cls. 訪問類的屬性

  • 也可以通過 cls. 呼叫其他的類方法

在類方法內部,可以直接使用 cls 訪問 類屬性 或者 呼叫類方法

2.靜態方法

  • 在開發時,如果需要在 中封裝一個方法,這個方法:
    • 不需要 訪問 例項屬性 或者呼叫 例項方法
    • 不需要 訪問 類屬性 或者呼叫 類方法
  • 這個時候,可以把這個方法封裝成一個 靜態方法

語法如下

@staticmethod
def 靜態方法名():
    pass
  • 靜態方法 需要用 修飾器 @staticmethod 來標識,告訴直譯器這是一個靜態方法
  • 通過 類名. 呼叫 靜態方法

4.總結

  • 例項方法 —— 方法內部需要訪問 例項屬性

  • 例項方法 內部可以使用 類名. 訪問類屬性

  • 類方法 —— 方法內部 需要訪問 類屬性

  • 靜態方法 —— 方法內部,不需要訪問 例項屬性類屬性

提問

如果方法內部 即需要訪問 例項屬性,又需要訪問 類屬性,應該定義成什麼方法?

答案

  • 應該定義 例項方法
  • 因為,類只有一個,在 例項方法 內部可以使用 類名. 訪問類屬性

五、繼承

1.概念

  • 子類 擁有 父類 的所有 方法屬性
  • 可以單繼承和多繼承

2.語法

class 類名(父類名):

    pass
  • 子類 繼承自 父類,可以直接 享受 父類中已經封裝好的方法,不需要再次開發
  • 子類 中應該根據 職責,封裝 子類特有的 屬性和方法

3.專業術語

  • Dog 類是 Animal 類的子類Animal 類是 Dog 類的父類Dog 類從 Animal繼承
  • Dog 類是 Animal 類的派生類Animal 類是 Dog 類的基類Dog 類從 Animal派生

4.繼承的傳遞性

  • C 類從 B 類繼承,B 類又從 A 類繼承
  • 那麼 C 類就具有 B 類和 A 類的所有屬性和方法

子類 擁有 父類 以及 父類的父類 中封裝的所有 屬性方法

提問: 哮天犬 能夠呼叫 Cat 類中定義的 catch 方法嗎?

5.方法的重寫

  • 子類 擁有 父類 的所有 方法屬性
  • 子類 繼承自 父類,可以直接 享受 父類中已經封裝好的方法,不需要再次開發

應用場景

  • 父類 的方法實現不能滿足子類需求時,可以對方法進行 重寫(override)

重寫 父類方法有兩種情況:

  • 覆蓋 父類的方法

  • 對父類方法進行 擴充套件

6.覆蓋父類的方法

  • 如果在開發中,父類的方法實現子類的方法實現完全不同
  • 就可以使用 覆蓋 的方式,在子類中 重新編寫 父類的方法實現

具體的實現方式,就相當於在 子類中 定義了一個 和父類同名的方法並且實現

重寫之後,在執行時,只會呼叫 子類中重寫的方法,而不再會呼叫 父類封裝的方法

7.對父類方法進行 擴充套件

  • 如果在開發中,子類的方法實現包含 父類的方法實現

    • 父類原本封裝的方法實現子類方法的一部分
  • 就可以使用 擴充套件 的方式

    • 在子類中 重寫 父類的方法

    • 在需要的位置使用 super().父類方法 來呼叫父類方法的執行

    • 程式碼其他的位置針對子類的需求,編寫 子類特有的程式碼實現

"""繼承。
父類  子類

繼承如何表示:
class 子類名(父類名):
    pass


子類可以實現自己獨有的方法。

子類可以覆蓋父類的方法。 ==》 重寫

super() 超繼承: 使用父類當中的方法。

"""

class Phone:
    """手機"""

    def __init__(self, number):
        self.number = number

    def call(self, to, recorded=False):
        """打電話"""
        print(" {} 給 {} 打電話。。".format(self.number, to))

        if recorded:
            self.record()

    def record(self):
        """錄音"""
        print("{} 正在錄音".format(self.number))


class SmartPhone(Phone):
    """智慧手機"""

    def __init__(self, number, brand):
        self.number = number
        self.brand = brand

    def watch_movie(self, name):
        print("正在看電影{}".format(name))


class Iphone(SmartPhone):
    # brand = '蘋果'

    def __init__(self, number):
        super().__init__(number, '蘋果')
        # self.number = number
        # self.brand = "蘋果"

    def face_time(self):
        """錄屏,直播"""
        print("{} 正在直播".format(self.number))

    def call(self, to, recorded=False, face=False):
        """打電話既可以錄音,也可以facetime"""
        # print(" {} 給 {} 打電話。。".format(self.number, to))
        #
        # if recorded:
        #     self.record()

        super().call(to, recorded=False)

        if face:
            self.face_time()



# normal_phone = Phone('1')
# print(normal_phone.record())
#
# # 父類當中不能呼叫子類方法。
# # normal_phone.watch_movie("紅海行動")
#
# smart_phone = SmartPhone("2")
# print(smart_phone.record())
# print(smart_phone.number)
# smart_phone.watch_movie('小偷家族')


# 當子類和父類具有同樣的方法或者屬性的時候。
# 父類還是用父類的, 子類不再有父類的,而是用自己的。
# normal = Phone("1")
# smart = SmartPhone(2, '華為')

# 重寫例子2
iphone = Iphone('蘋果5')
iphone.call('開始', face=True)

# smart = SmartPhone("123", '華為')
# smart.call('李三', face=True)

8.關於 super

  • Pythonsuper 是一個 特殊的類
  • super() 就是使用 super 類建立出來的物件
  • 最常 使用的場景就是在 重寫父類方法時,呼叫 在父類中封裝的方法實現
       # super預設會呼叫第一個父類的方法(適用於單繼承 或者只想使用第一個父類的方法)
    
          # 02 方式 適用於新式類
          # 格式: super(子類類名, self).父類方法名()
    
    
    
    

總結

類的命名:大駝峰命名: 兩個單詞的首字母大寫。
class PerfectManHelp

函式的命名:下劃線命名:perfect_man_help

變數命名:下劃線命名.

相關文章