日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python学习笔记:面向对象高级编程(上)

發布時間:2025/3/15 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python学习笔记:面向对象高级编程(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

最近在學習深度學習,已經跑出了幾個模型,但Pyhton的基礎不夠扎實,因此,開始補習Python了,大家都推薦廖雪峰的課程,因此,開始了學習,但光學有沒有用,還要和大家討論一下,因此,寫下這些帖子,廖雪峰的課程連接在這里:廖雪峰
Python的相關介紹,以及它的歷史故事和運行機制,可以參見這篇:python介紹
Python的安裝可以參見這篇:Python安裝
Python的運行模式以及輸入輸出可以參見這篇:Python IO
Python的基礎概念介紹,可以參見這篇:Python 基礎
Python字符串和編碼的介紹,可以參見這篇:Python字符串與編碼
Python基本數據結構:list和tuple介紹,可以參見這篇:Python list和tuple
Python控制語句介紹:ifelse,可以參見這篇:Python 條件判斷
Python控制語句介紹:循環實現,可以參見這篇:Python循環語句
Python數據結構:dict和set介紹Python數據結構dict和set
Python函數相關:Python函數
Python高階特性:Python高級特性
Python高階函數:Python高階函數
Python匿名函數:Python匿名函數
Python裝飾器:Python裝飾器
Python偏函數:Python偏函數
Python模塊:Python模塊
Python面向對象編程(1):Python面向對象
Python面向對象編程(2):Python面向對象(2)
Python面向對象編程(3):Python面向對象(3)
Python面向對象編程(4):Pyhton面向對象(4)
目錄:

  • 前言
  • 面向對象高級編程
  • 使用slots
  • 使用@property
  • 多重繼承
    • MixIn
    • 小結

面向對象高級編程

數據封裝、繼承和多態只是面向對象程序設計中最基礎的3個概念。在Python中,面向對象還有很多高級特性,允許我們寫出非常強大的功能。
我們會討論多重繼承、定制類、元類等概念。

使用slots

正常情況下,當我們定義了一個class,創建了一個class的實例后,我們可以給該實例綁定任何屬性和方法,這就是動態語言的靈活性。先定義class:

class Student(object):pass

然后,嘗試給實例綁定一個屬性:

>>> s = Student() >>> s.name = 'Mike' # 動態給實例綁定一個屬性 >>> print(s.name) Mike

還可以嘗試給實例綁定一個方法:

>>> def set_age(self, age): # 定義一個函數作為實例方法 ... self.age = age ... >>> from types import MethodType >>> s.set_age = MethodType(set_age, s) # 給實例綁定一個方法 >>> s.set_age(25) # 調用實例方法 >>> s.age # 測試結果 25

但是,給一個實例綁定的方法,對另一個實例是不起作用的:
對于實例,方法私有

>>> s2 = Student() # 創建新的實例 >>> s2.set_age(25) # 嘗試調用方法 Traceback (most recent call last):File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'set_age'

為了給所有實例都綁定方法,可以給class綁定方法:

>>> def set_score(self, score): ... self.score = score ... >>> Student.set_score = set_score

給class綁定方法后,所有實例均可調用:

>>> s.set_score(100) >>> s.score 100 >>> s2.set_score(99) >>> s2.score 99

通常情況下,上面的set_score方法可以直接定義在class中,但動態綁定允許我們在程序運行的過程中動態給class加上功能,這在靜態語言中很難實現。

使用__slots__

但是,如果我們想要限制實例的屬性怎么辦?比如,只允許對Student實例添加name和age屬性。

為了達到限制的目的,Python允許在定義class的時候,定義一個特殊的_slots_變量,來限制該class實例能添加的屬性:

class Student(object):__slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱

然后,我們試試:

>>> s = Student() # 創建新的實例 >>> s.name = 'Michael' # 綁定屬性'name' >>> s.age = 25 # 綁定屬性'age' >>> s.score = 99 # 綁定屬性'score' Traceback (most recent call last):File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score'

由于’score’沒有被放到slots中,所以不能綁定score屬性,試圖綁定score將得到AttributeError的錯誤。
如果加入了方法,比如

def set_age(self, age): # 定義一個函數作為實例方法self.age = age

雖然沒有增加,age這個屬性,但因為有了set_age方法,還是會增加age這個屬性。
使用slots要注意,slots定義限制屬性僅對當前類實例起作用,對繼承的子類是不起作用的:

>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999

除非在子類中也定義_slots_,這樣,子類實例允許定義的屬性就是自身的_slots_加上父類的_slots_

使用@property

在綁定屬性時,如果我們直接把屬性暴露出去,雖然寫起來很簡單,但是,沒辦法檢查參數,導致可以把成績隨便改:

s = Student() s.score = 9999

這顯然不合邏輯。為了限制score的范圍,可以通過一個set_score()方法來設置成績,再通過一個get_score()來獲取成績,這樣,在set_score()方法里,就可以檢查參數:

class Student(object):def get_score(self):return self._scoredef set_score(self, value):if not isinstance(value, int):raise ValueError('score must be an integer!')if value < 0 or value > 100:raise ValueError('score must between 0 ~ 100!')self._score = value現在,對任意的Student實例進行操作,就不能隨心所欲地設置score了:>>> s = Student() >>> s.set_score(60) # ok! >>> s.get_score() 60 >>> s.set_score(9999) Traceback (most recent call last):... ValueError: score must between 0 ~ 100!

但是,上面的調用方法又略顯復雜,沒有直接用屬性這么直接簡單。
有沒有既能檢查參數,又可以用類似屬性這樣簡單的方式來訪問類的變量呢?對于追求完美的Python程序員來說,這是必須要做到的!
還記得裝飾器(decorator)可以給函數動態加上功能嗎?對于類的方法,裝飾器一樣起作用。Python內置的@property裝飾器就是負責把一個方法變成屬性調用的:

class Student(object):@propertydef score(self):return self._score@score.setterdef score(self, value):if not isinstance(value, int):raise ValueError('score must be an integer!')if value < 0 or value > 100:raise ValueError('score must between 0 ~ 100!')self._score = value

@property的實現比較復雜,我們先考察如何使用。把一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身又創建了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值,于是,我們就擁有一個可控的屬性操作:

>>> s = Student() >>> s.score = 60 # OK,實際轉化為s.set_score(60) >>> s.score # OK,實際轉化為s.get_score() 60 >>> s.score = 9999 Traceback (most recent call last):... ValueError: score must between 0 ~ 100!

注意到這個神奇的@property,我們在對實例屬性操作的時候,就知道該屬性很可能不是直接暴露的,而是通過getter和setter方法來實現的。
這個@property實現了一個非常重要的功能,根據操作動態實現了不同的函數,函數對人是一樣的,但程序對其進行了分類,實現了智能操作。
還可以定義只讀屬性,只定義getter方法,不定義setter方法就是一個只讀屬性:

class Student(object):@propertydef birth(self):return self._birth@birth.setterdef birth(self, value):self._birth = value@propertydef age(self):return 2015 - self._birth

上面的birth是可讀寫屬性,而age就是一個只讀屬性,因為age可以根據birth和當前時間計算出來。
小結
@property廣泛應用在類的定義中,可以讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減少了出錯的可能性。

多重繼承

繼承是面向對象編程的一個重要的方式,因為通過繼承,子類就可以擴展父類的功能。
在對一個實體進行分類的時候,會有各種判斷標準,如果把每個標準都用上,類的設計就太麻煩了,因此,我們可以實現一個多重繼承,主分類和特定屬性分類。
回憶一下Animal類層次的設計,假設我們要實現以下4種動物:

Dog - 狗狗;Bat - 蝙蝠;Parrot - 鸚鵡;Ostrich - 鴕鳥。

如果按照哺乳動物和鳥類歸類,我們可以設計出這樣的類的層次:

┌───────────────┐│ Animal │└───────────────┘│┌────────────┴────────────┐│ │▼ ▼┌─────────────┐ ┌─────────────┐│ Mammal │ │ Bird │└─────────────┘ └─────────────┘│ │┌─────┴──────┐ ┌─────┴──────┐│ │ │ │▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Dog │ │ Bat │ │ Parrot │ │ Ostrich │ └─────────┘ └─────────┘ └─────────┘ └─────────┘

但是如果按照“能跑”和“能飛”來歸類,我們就應該設計出這樣的類的層次:

┌───────────────┐│ Animal │└───────────────┘│┌────────────┴────────────┐│ │▼ ▼┌─────────────┐ ┌─────────────┐│ Runnable │ │ Flyable │└─────────────┘ └─────────────┘│ │┌─────┴──────┐ ┌─────┴──────┐│ │ │ │▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Dog │ │ Ostrich │ │ Parrot │ │ Bat │ └─────────┘ └─────────┘ └─────────┘ └─────────┘

如果要把上面的兩種分類都包含進來,我們就得設計更多的層次:

哺乳類:能跑的哺乳類,能飛的哺乳類; 鳥類:能跑的鳥類,能飛的鳥類。

這么一來,類的層次就復雜了:

┌───────────────┐│ Animal │└───────────────┘│┌────────────┴────────────┐│ │▼ ▼┌─────────────┐ ┌─────────────┐│ Mammal │ │ Bird │└─────────────┘ └─────────────┘│ │┌─────┴──────┐ ┌─────┴──────┐│ │ │ │▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ MRun │ │ MFly │ │ BRun │ │ BFly │ └─────────┘ └─────────┘ └─────────┘ └─────────┘│ │ │ ││ │ │ │▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Dog │ │ Bat │ │ Ostrich │ │ Parrot │ └─────────┘ └─────────┘ └─────────┘ └─────────┘

如果要再增加“寵物類”和“非寵物類”,這么搞下去,類的數量會呈指數增長,很明顯這樣設計是不行的。

正確的做法是采用多重繼承。首先,主要的類層次仍按照哺乳類和鳥類設計:

class Animal(object):pass# 大類: class Mammal(Animal):passclass Bird(Animal):pass# 各種動物: class Dog(Mammal):passclass Bat(Mammal):passclass Parrot(Bird):passclass Ostrich(Bird):pass

現在,我們要給動物再加上Runnable和Flyable的功能,只需要先定義好Runnable和Flyable的類:

class Runnable(object):def run(self):print('Running...')class Flyable(object):def fly(self):print('Flying...')

對于需要Runnable功能的動物,就多繼承一個Runnable,例如Dog:

class Dog(Mammal, Runnable):pass

對于需要Flyable功能的動物,就多繼承一個Flyable,例如Bat:

class Bat(Mammal, Flyable):pass

通過多重繼承,一個子類就可以同時獲得多個父類的所有功能。

MixIn

在設計類的繼承關系時,通常,主線都是單一繼承下來的,例如,Ostrich繼承自Bird。但是,如果需要“混入”額外的功能,通過多重繼承就可以實現,比如,讓Ostrich除了繼承自Bird外,再同時繼承Runnable。這種設計通常稱之為MixIn。

為了更好地看出繼承關系,我們把Runnable和Flyable改為RunnableMixIn和FlyableMixIn。類似的,你還可以定義出肉食動物CarnivorousMixIn和植食動物HerbivoresMixIn,讓某個動物同時擁有好幾個MixIn:

class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):pass

MixIn的目的就是給一個類增加多個功能,這樣,在設計類的時候,我們優先考慮通過多重繼承來組合多個MixIn的功能,而不是設計多層次的復雜的繼承關系。

Python自帶的很多庫也使用了MixIn。舉個例子,Python自帶了TCPServer和UDPServer這兩類網絡服務,而要同時服務多個用戶就必須使用多進程或多線程模型,這兩種模型由ForkingMixIn和ThreadingMixIn提供。通過組合,我們就可以創造出合適的服務來。

比如,編寫一個多進程模式的TCP服務,定義如下:

class MyTCPServer(TCPServer, ForkingMixIn):pass

編寫一個多線程模式的UDP服務,定義如下:

class MyUDPServer(UDPServer, ThreadingMixIn):pass

如果你打算搞一個更先進的協程模型,可以編寫一個CoroutineMixIn:

class MyTCPServer(TCPServer, CoroutineMixIn):pass

這樣一來,我們不需要復雜而龐大的繼承鏈,只要選擇組合不同的類的功能,就可以快速構造出所需的子類。

小結

由于Python允許使用多重繼承,因此,MixIn就是一種常見的設計。
只允許單一繼承的語言(如Java)不能使用MixIn的設計。

總結

以上是生活随笔為你收集整理的Python学习笔记:面向对象高级编程(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。