Python學習之面向對象編程詳解

什麼是面向對象編程(類)

利用(面向)對象的(屬性和方法)去進行編碼的過程即面向對象編程

自定義對象數據類型就是面向對象中的類(class)的概念

類的關鍵字 – class

class 關鍵字用來聲明類,類的名稱首字母大寫,多單詞的情況下每個單詞首字母大寫(即駝峰命名法)。在我們一開始學習 Python 的時候說過,要盡量避免使用 駝峰命名法 ,但 類 的命名是一個特例,類 的命名可以使用駝峰命名。

類的定義與使用

類的定義示例如下:

class Nmae(object):        
# class關鍵字 + 類名(首字母大寫) + 小括號(括號內填寫 object:為python中的通用對象,書寫通用對象的 class 會帶有更多的內置功能) + 冒號
    
    變量 = 變量的值
    # 可以定義 類 的變量
    
    def func(self):
        do
    # 也可以定義 類 的函數:類函數內有個必傳參數 'self' ,一定要書寫在類函數的第一參數位,這是 python 內部的語法規定
    
# 需要註意的是 類的屬性與函數的縮進要統一

類的使用示例如下:

# 定義一個動物類;動物類中定義一個變量;定義一個 跑 的函數(屬性)

class Animal(object):	# 定義一個類
    name = '哈士奇'		# 類變量(類屬性)
    
    def run(self):		# 類函數:將 self 作為第一個參數傳入 類函數 'run()'
        print(f'{self.name} can run')	
        				# 'self.name'為類屬性,如果不加上'self.'則不會找到類屬性;如果想要在類函數中調用類屬性就必須加上'self.'
            			# 'self' 參數具備兩個功能
                		# 1.可以幫助我們調用類屬性
                    	# 2.將使用 self 的函數調用到類中,如果我們有另一個函數,可以在另一個函數中通過 'self.' 來進行調用 run 函數
                        
dog = Animal()		# 類的實例化
print(dog.name)		# 通過實例化進行屬性調用

dog.run()			# 通過實例化,進行函數調用

# >>> 執行結果如下:
# >>> 哈士奇
# >>> 哈士奇 can run

類的參數 – self

在類裡面,所有實例方法都需要加 self 參數,且排在第一個,有且僅有一個。

self 參數的含義 :在類中定義的方法,第一個參數 self 指向調用該方法的實例對象,在方法中通過 self.屬性 這樣的形式訪問對象的實例屬性

  • self 是 類函數 中的必傳參數,且必須放在第一個參數位置
  • self 是一個對象,它代表著實例化的變量自身
  • self 可以直接通過點(.)來定義一個類變量 如 self.name = Neo ,如果在函數體內定義變量可以通過 self + . +變量名 來進行賦值。
  • self 中的變量與含有 self參數的函數可以在類中的任何一個函數內隨意調用
  • 非函數中定義的變量在定時的時候不需要使用 self

如何理解 self 參數

類比

  • 如果把 類 比作造房子的圖紙
  • 類實例化 後的對象是真正可以住的房子
  • 根據一張圖紙(類),可以設計出成千上萬的房子(實例對象)
  • 每個房子長相都是類似的(都有相同的實例屬性和實例方法),但它們都有各自的主人
  • 如何區分不同的房子:通過 self 參數,可以保證每個房子的主任僅能進入自己的房子(每個實例對象隻能調用自己的實例屬性和實例方法)

重點

  • 一個類可以產生多個實例對象,當某個實例對象調用實例方法,該對象會把自身的引用作為第一個參數自動傳遞給該方法
  • 換句話說:Python 會自動將實例方法的第一個參數指向調用該方法的對象
  • 這樣,Python 解釋器就知道到底要執行哪個對象的實例方法瞭
  • 調用實例方法的時候,不需要手動為第一個參數傳值

可能大傢還不是很理解,根據類的兩個關鍵要素屬性和方法,具體來使用self看看實際應用效果:

class Persion(object):
    name = None
    age = None

    def run(self):
        print(f'{self.name} 的健身項目是\'跑步\'')

    def swim(self):
        print(f'{self.name} 的健身項目是\'遊泳\'')


neo = Persion()
neo.name = 'Neo'
neo.run()

# >>> 執行結果如下:
# >>> Neo 的健身項目是'跑步'

我們再重新實例化一個對象,看看這個新的實例化對象是否同步瞭 neo.name

class Persion(object):
    name = None
    age = None

    def run(self):
        print(f'{self.name} 的健身項目是\'跑步\'')

    def swim(self):
        print(f'{self.name} 的健身項目是\'遊泳\'')


neo = Persion()
neo.name = 'Neo'
neo.run()


jack = Persion()
jack.run()

# >>> 執行結果如下:
# >>> Neo 的健身項目是'跑步'
# >>> None 的健身項目是'跑步'

從輸出結果可以看到 我們修改的 neo 實例化對象的對應的 name 的值僅作用於自己的實例,而 Persion 類,與新的 jack 實例化對象並沒有受到影響。

所以即使使用新的對象進行實例化,還是需要新的實例化對象來修改類的屬性,來達到我們自己想要的效果。其實很好理解,都是人類,但是每個人的個體化都是不同的。所以他們擁有人類的共同屬性後 (name,age) ,也可以自定義自己的屬性。

現在我們的 Persion 類 定義瞭兩個屬性 'name' 與 'age' ,如果我們再添加一個屬性呢? ,其實是可以的。現在我們針對 'Jack' 增加一個自定義屬性 ,嘗試一下。

class Persion(object):
    name = None
    age = None

    def run(self):
        print(f'{self.name} 的健身項目是\'跑步\'')

    def swim(self):
        print(f'{self.name} 的健身項目是\'遊泳\'')


neo = Persion()
neo.name = 'Neo'
neo.run()


jack = Persion()
jack.top = 180
print('\'Jack\'的身高是', jack.top)

# >>> 執行結果如下
# >>> Neo 的健身項目是'跑步'
# >>> 'Jack'的身高是 180


print('\'Neo\'的身高是', neo.top)
# >>> 執行結果如下:
# >>> AttributeError: 'Persion' object has no attribute 'top'

從上面的 jack.top 與 neo.top 的自定義屬性,我們發現三件事。

1.實例化對象可以自定義屬性

2.每個實例化對象自己定義的屬性與其他實例化對象不通用。

3.Persion類在實例化對象之後,依然隻有自己的兩個屬性 (name 和 age) ,實例化對象自定義的屬性僅作用於自己,並不影響 類 。

self 的解析與總結

說實話,關於 Python 中的 self 我一開始接觸的時候,也是給我搞的雲裡霧繞、五迷三道的…這裡做個總結,希望對同樣雲裡霧繞、五迷三道的童鞋有所幫助。

  • Python 中 self 代表的是 類的示例 ; self 在定義類的方法時是必須有的,雖然在調用時不必傳入相應的參數。
  • Python 中 self 隻有在針對 類 的情況下,才是有意義的。
  • self 隻能用在 python 類 的方法中。

具體的舉例說明如下:

屬性

關於屬性 – 1:如果變量定義在類下面而不是類的方法下面,那這個變量既是類的屬性也是類實例的屬性。

class Cat(object):
    eyes = '有2隻眼睛'
    legs = '有4條腿'
    tail = '有1隻尾巴'


dragonLi = Cat()
dragonLi.name = '貍花貓'
dragonLi_eyes = dragonLi.eyes
dragonLi_legs = dragonLi.legs
dragonLi_tail = dragonLi.tail

print(' 貓 ' + Cat.eyes, Cat.legs, Cat.tail)

print(dragonLi.name, dragonLi_eyes, dragonLi_legs, dragonLi_tail)

# >>> 執行結果如下:
# >>>  貓 有2隻眼睛 有4條腿 有1隻尾巴
# >>> 貍花貓 有2隻眼睛 有4條腿 有1隻尾巴

關於屬性 – 2:如果變量定義在類的方法下面,如果加瞭self,那這個變量就是類實例的屬性,不是類的屬性;如果沒有加self,這個變量隻是這個方法的局部變量,既不是類的屬性也不是類實例的屬性。

class Cat(object):
    eyes = '有2隻眼睛'
    legs = '有4條腿'
    tail = '有1隻尾巴'

    def __init__(self):			# 關於__init__() 會在下面的 '類的構造器'有詳細講解
        self.color_01 = '黃棕色'
        color_02 = '黑棕色'


dragonLi = Cat()
dragonLi_color_01 = dragonLi.color_01
print('貍花貓有兩種披毛顏色,一種是:', dragonLi_color_01)

# >>> 執行結果如下:
# >>> 貍花貓有兩種披毛顏色,一種是: 黃棕色

dragonLi_color_02 = dragonLi.color_02
print('貍花貓有兩種披毛顏色,另一種是:', dragonLi_color_02)

# >>> 執行結果如下:
# >>> AttributeError: 'Cat' object has no attribute 'color_02'.

方法

關於方法1:如果在類中定義函數時加瞭self,那這個函數是類實例的方法,而不是類的方法。

class Cat(object):

    def eat(self):
        print('愛吃魚')


dragonLi = Cat()
dragonLi.eat()

# >>> 執行結果如下:
# >>> 愛吃魚

Cat.cat()

# >>> 執行結果如下:
# >>> TypeError: Cat.eat() missing 1 required positional argument: 'self'

關於方法2:如果在類中定義函數時候沒有加self,那這個函數就隻是類的方法,而不是類實例的方法。

class Cat(object):

    def eat():
        print('愛吃魚')


Cat.eat()

# >>> 執行結果如下:
# >>> 愛吃魚

dragonLi = Cat()
dragonLi.eat()

# >>> 執行結果如下:
# >>> TypeError: Cat.eat() takes 0 positional arguments but 1 was given

小結

屬性:

  • 如果變量定義在類下面而不是類的方法下面,那這個變量既是類的屬性也是類實例的屬性。
  • 如果變量定義在類的方法下面,如果加瞭self,那這個變量就是類實例的屬性,不是類的屬性;如果沒有加self,這個變量隻是這個方法的局部變量,既不是類的屬性也不是類實例的屬性。

方法:

  • 如果在類中定義函數時加瞭self,那這個函數是類實例的方法,而不是類的方法。
  • 如果在類中定義函數時候沒有加self,那這個函數就隻是類的方法,而不是類實例的方法。

類的構造函數

前面我們瞭解瞭 類的創建、類的屬性、類函數的使用方法,現在我們再來看看類的構造函數。

什麼是類的構造函數? —> 構造函數是類中的一種默認函數,通過定義它可以在 類實例化 的同時,將參數傳入類中。(類似於函數執行的時候可以傳一些參數)

構造函數的創建方法

重點:構造函數依然要在 類 中定義

def __init__(self, a, b)        # def關鍵字 + __init__ + 小括號(括號內第一個傳入的依然是 self ,後面再跟上希望實例化時傳入的參數)
    self.a = a                    # 在構造函數裡,將參數綁定在 self 中,將變量通過 self 綁定之後,就可以在類的各個函數中進行調用瞭
    self.b = b

構造函數的用法,示例如下:

class Test(object):
    
    def __init__(self, a):		# __init__ 構造函數一定要寫在第一個,這是一個很好的編程規范
        self.a = a
        
    def run(self):
        print(self.a)
        
        
test = Test(1)
test.run()

# >>> 執行結果如下:
# >>> 1


test_02 = Test('Hello')
test_02.run()

# >>> 執行結果如下:
# >>> Hello

接下來我們再使用 構造函數 針對前面我們創建的 Cat 類進行修改

class Cat(object):

    def __init__(self, eyes, legs, tail, color='黃棕色'):
        self.eyes = eyes
        self.legs = legs
        self.tail = tail
        self.color = color

    def show_cat(self):
        self.work = '抓老鼠'
        print('貓的通用屬性為', self.eyes, self.legs, self.tail)


dragonLi = Cat('2隻眼睛', '4條腿', '1隻尾巴')
dragonLi.show_cat()

# >>> 執行結果如下:
# >>> 貓的通用屬性為 2隻眼睛 4條腿 1隻尾巴 黃棕色


dragonLi.name = '貍花貓'
dragonLi.color = '虎斑色'

print(dragonLi.name, dragonLi.eyes, dragonLi.legs, dragonLi.tail, dragonLi.color, dragonLi.work)

# >>> 執行結果如下:
# >>> 貍花貓 2隻眼睛 4條腿 1隻尾巴 虎斑色 抓老鼠

關於對象的生命周期

註意:這裡說的對象的生命周期,指的是實例化的對象。

之前我們提到過,當一個變量不使用的時候就會被內存管傢清理掉。 接下來我們就來看看一個變量的一生,從出現到消亡的過程。之所以在這裡插上這一章節介紹 對象的生命周期 ,也是為瞭更好的理解對象, 從而更好的使用他們。

我們之前學習的 深拷貝與淺拷貝, 淺拷貝是創建一個新的內存地址, 而深拷貝是使用之前已經定好的變量。

通過對一個對象的生命周期的理解, 可以讓我們權衡是終結一個對象還是繼續使用它們。

我們通過構造函數完成一個對象的實例化,這個時候一個對象的生命周期就開始瞭,在這個時候內存管傢發現有一個對象的加入就會為這個實例化的對象分配一個內存地址(也就是實例化對象在內存中的傢)。

接下裡我們就可以操作這個對象,可以調用它的內置函數還有功能。當我們不使用這個對象的時候,一般有兩種情況;

第一種是這個內存塊中的值沒有變量與之綁定瞭,比如當我們的一個變量的默認值被一個函數調用後變量有瞭新的值,這個時候變量原有的默認值與變量不再是賦值關系瞭。

第二種是當我們執行完瞭所有的程序,也就是代碼已經執行到瞭最後一行。 Python 解釋器發現已經處理完瞭所有的業務,這個時候腳本就會停止處理並釋放腳本中所有的對象,釋放所有的對象其實就是告知內存管傢,內存管傢就會自動處理這些對象的內存地址。

以上兩種情況的統一表現形態都是不再使用這些對象,這個時候每個對象中自帶的內置函數 __del__ (兩個下劃線)就會被調用,通知內存管傢從內存中釋放每個對象對應的內存塊。這就是整個對象的生命周期。

無論是字符串、數字、列表、元組、字典、集合,甚至佈爾類型與空類型,我們知道 Python 中一切皆是對象,所以它們也是按照這個規律存在於消亡。

Python 中的內存管理都是自動去完成的,所以我們並不需要特意的去對它進行專門的處理。

關於 __del__ 函數並不需要我們書寫和定義,當我們實例化一個對象之後,它就會默認存在,並擁有自動通知內存管傢清理內存的功能。這也是 Python 的特點之一。

以上就是Python學習之面向對象編程詳解的詳細內容,更多關於Python面向對象編程的資料請關註WalkonNet其它相關文章!

推薦閱讀: