04-python中的面向對象編程-02

04-python中的面向對象編程-02

​ 之前 我們談到面向對象的一些基礎知識,今天 我們 繼續探討 python 中的面向對象編程第二篇, 上一篇文章 簡單介紹了 類, 對象,以及 類以及對象的關係 ,今天更加詳細的介紹 Python語言中 面向對象 的相關細節.

​ 面向對象 一般都會提到 封裝 ,繼承,多態 這些概念 看起來 比較抽象, 其實之後隨着 你慢慢地 使用就會發現 沒有那麼難.

基礎知識

介紹面向對象 之前 先介紹一下 一下,python 中面向對象的編程一些語法知識

前一篇的文章,我們講了 類 和對象的關係, 什麼是類,什麼是對象.

類相當於一個模板, 可以用來生成一堆對象. 每個對象的屬性值 可以相同也可以不同.

還以之前的代碼爲例:

class Person:

    def __init__(self, name, height, weight):
        self.name = name
        self.height = height
        self.weight = weight

    def cook(self):
        print(f"{self.name} is cooking")

    def eat(self):
        print(f"{self.name} is eating")
        self.weight = self.weight + 2

    def swim(self):
        print(f"{self.name} is  swimming")
        self.weight = self.weight - 1

上面 我定義了Person類, 有一些方法 cook ,eat swim

你可能 對 裏面 方法 比較好奇, 這裏方法 和普通函數的區別是什麼?

實例方法 就是綁定 實例對象的一個函數, 這個函數供 類生成的實例調用.

而普通函數 就是一個函數,沒有綁定 任何對象,可以直接調用.

可以看到 每一個方法 第一個參數都有 self 這個參數

    def cook(self):
        print(f"{self.name} is cooking")

以這個方法爲例 ,這裏self 是什麼呢? 你可能會好奇. 這裏self 即 將要 生成對象,這裏用self 表示.

下面我來實例化 這個類. 當實例化完成之後,p 三個屬性被賦值了.

p = Person(name='frank',height=165,weight=55)

p.name == 'frank' 當 對象調用 cook() 方法的時候

p.cook() 相當於是這樣 cook(p) , 看到沒有 這就是 self 的作用 , self 就表示 當前對象.

假設 又有一個實例

p2 = Person(name='frank2',height=165,weight=50)
p2.name   # frank2

p2.cook() 相當於 cook(p2) 這樣調用, 這個時候 你能夠看明白了嗎?

self 就是 誰調用了這個方法, 誰就是這個self .

類 用來 創建一個個對象,畫了一個簡單的圖,來說明一下, 當 執行 Person(name='frank2',height=165,weight=55) 這個代碼的時候,就會 調用 __init__ 方法 來生成 一個對象, 並且 把這些值 爲複製 到 這個對象的屬性上面。 類相當於一個工廠 用來生產 一批相同的對象,只是對象 初始化的 屬性不同,對象的行爲(方法) 是完全一樣的。

類和對象關係-img-01

你可能有疑問, 爲啥是self ,可以使用其他的關鍵字來替換嗎?

當然 可以 其實在其他的語言中就用的 不一樣,比如 Java 語言 使用 this 代表當前對象. 這裏 self 就是一個名稱, 換成其他名字也是可以的, 但是幾乎很少人會這樣做,因爲大家都用self 作爲當前的實例, 儘管 你可以換名稱, 但並不建議你 這麼做. 畢竟 大家都用self 作爲當前的實例. (習慣的力量)

實例方法

>>> 
... class Person:
... 
...     def __init__(this, name, height, weight):
...         this.name = name
...         this.height = height
...         this.weight = weight
... 
...     def cook(this):
...         print(f"{this.name} is cooking")
...         
>>> 
>>> p = Person(name='frank',height=165,weight=55)
>>> p.cook()
frank is cooking

類方法

繼續 聊聊 下一個 方法 叫類方法, 類方法是什麼意思,就是這個方法 屬於類進行調用不需要用實例調用,這個方法 所有對象 公用這個方法.

一般形式 需要用一個 裝飾器進行修飾 classmethod

這裏 我在 Person 類定義一個 類屬性, description 這裏 來說明 這個類的簡要概括.

這個屬性 就是 這個類的所有的對象 公有, 這個時候 就要把 這個屬性 從 __init__ 方法裏面 抽出來,因爲 每個對象都是 一樣的,就沒有必要 在 __init__ 了.

所以 類方法 也是這個意思, 類方法 是希望 這個類來調用這個方法, 所有 由這個類生成的對象,都是可以 使用這個方法的, 並且 這個方法 只會操作 類屬性,而不會操作 自身的屬性.

這樣的方法 就是類方法

class Person:
    # 類屬性
    description = 'this is a person'

    def __init__(this, name, height, weight):
        this.name = name
        this.height = height
        this.weight = weight
		
    # 實例方法
    def cook(this):
        print(f"{this.name} is cooking")
		
    
    # 類方法 供類調用
    @classmethod
    def get_description(cls):
        return cls.description
      
      
      
if __name__ == '__main__':
    p = Person(name='frank', height=165, weight=55)
  

上面 get_description 就是一個類方法, 從面向對象的角度來說,這個方法 用來操作 屬於類的數據,而不是 對象的數據.

在控制檯 裏面 演示

>>> 
>>> p = Person(name='frank', height=165, weight=55)
>>> 
>>> p.get_description()
'this is a person'
>>> p2 = Person(name='frank2', height=165, weight=55)
>>> p2.get_description()
'this is a person'
>>> p3 = Person(name='frank3', height=165, weight=55)
>>> Person.get_description()
'this is a person'

可以看出 無論通過 生成一個對象來調用,還是 直接 使用類名 去調用這個方法 都是可以的, 這裏返回 都是這個類 的描述.

所以類方法 可以使用類 進行調用,也可以使用對象進行調用. 從上面的例子可以看出來.

靜態方法

還有一種方法 叫做 靜態方法 ; 靜態方法 是什麼意思呢?

簡單來說 這個方法 本來就是 一個函數,和這個類沒有任何關係, 即這個方法 不操作 這個類的屬性( 數據),以及對象的屬性(數據) , 就是這樣, 這樣的方法 就是靜態方法 .

靜態方法 唯一的好處就是 把 這個函數 放在 類的作用域內, 對於 其他的類 這個方法 就不可見了.

看例子:

class Person:
    # 類屬性
    description = 'this is a person'

    def __init__(this, name, height, weight):
        this.name = name
        this.height = height
        this.weight = weight

    def cook(this):
        print(f"{this.name} is cooking")

    def eat(self):
        print(f"{self.name} is eating")
        self.weight = self.weight + 2

    def swim(self):
        print(f"{self.name} is  swimming")
        self.weight = self.weight - 1
		
    
    # 類方法 供類調用
    @classmethod
    def get_description(cls):
        return cls.description
	
  	# 靜態方法
    @staticmethod
    def add(a, b):
        return a + b

上面 add 方法 ,就是一個靜態方法, 爲啥呢? 因爲這個方法,沒有對 這個類的數據,或者對象的數據 進行操作.

所以這個方法就是 靜態方法, 這個時候 需要 用staticmethod修飾一下, 才能變成靜態方法.

靜態方法 如何調用?

靜態方法 可以直接使用類名 去調用, 或者使用對象 也是可以調用的.

>>> 
>>> p = Person(name='frank', height=165, weight=55)
... 
>>> 
>>> p.add(10,5)
15
>>> p.add(11,5)
16
>>> Person.add(1,2)
3

所以 靜態 方法 就是 相當於一個函數, 而只是 這個函數 寫在 class 裏面了而已, 所以 類是可以調用這個方法,同時 對象也是可以調用這個方法的.

方法類型 對象
類方法 可以調用 可以調用
靜態方法 可以調用 可以調用
實例方法 不可以調用 可以調用

簡單總結一下 , 在python 中 定義一個類,可以有很很多類型的方法 ,類方法,實例方法,靜態方法 。 要知道 每種方法 需要如何定義,以及每種方法的特點。

類方法 的一些要走的坑

# -*- coding: utf-8 -*- 

class A:
    name = 'A'

    @classmethod
    def get_cls_name(cls):
        return cls.name


if __name__ == '__main__':
    a1 = A()
    a2 = A()
    a3 = A()

    print(a1.get_cls_name())
    print(a2.get_cls_name())
    print(a3.get_cls_name())

    print("修改 a2.name")
    a2.name = 'a2'
    print(a2.get_cls_name())  # A
    print(a2.name)    # a2
    pass

我嘗試 修改 a2.name 的值 , 然後獲取值 發現 類A 中的name 並沒有修改。

原因是什麼呢? 其實 這源於 python 語言的動態性。

當執行 a2.name = ‘a2’ 然後 會在 a2 對象生成一個屬性name .

讓我們在Console 看下

... class A:
...     name = 'A'
... 
...     @classmethod
...     def get_cls_name(cls):
...         return cls.name
... 
>>> 
>>> 
>>> a1 = A()
... a2 = A()
... a3 = A()
>>> 
>>> 
>>> a1.get_cls_name()
'A'
>>> a2.get_cls_name()
'A'
>>> a3.get_cls_name()
'A'
>>> print("修改 a2.name")
... a2.name = 'a2'
修改 a2.name
>>> a2.name
'a2'
>>> a2.get_cls_name()
'A'

>>> a1.name
'A'
>>> a3.name
'A'




>>> pprint(dir(a2))
['__class__',
 	...
 '__weakref__',
 'get_cls_name',
 'name']


>>> pprint(dir(A))
['__class__',
 ...
 '__weakref__',
 'get_cls_name',
 'name']

>>> # 看下id 值
>>> id(A.name)
1886933580016
>>> id(a1.name)
1886933580016
>>> id(a2.name)
1886948838640

通過 dir 函數 去查看 a2,A 的屬性 發現 都有 name ,get_cls_name 這兩個屬性 。

然後查看 發現 a2.name 和 A.name 的id 值 是否一樣, 發現 是不一樣的。

a2.name = 'a2' 會在對象上面添加一個屬性name 這個屬性綁定在實例a2 上面了。 不會影響 a1, a3 ,通過 打印 id 值 ,就可以發現 A.name 與 a1.name 的id值是相同的。

所以這裏簡單 總結一下 ,不要想通過 實例對象 來修改類變量的值, 因爲 這樣只會重新一個屬性 綁定在 這個對象上面 。

這個原因是python語言的動態性。

總結

這篇文章 介紹了python 語言中 面向對象編程的基礎知識, 常見的幾種方法 . 比較簡單吧, 之後 我會繼續 寫這個專題,繼續 介紹 面向對象 的幾個特點 封裝 ,繼承 ,多態等 基本特性. 加油,好好學習.

分享快樂,留住感動. 2020-06-11 20:40:19 --frank
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章