面向對象初識④

?

?

抽象類與接口類

?

接口類

?

繼承有兩種用途:

?

一:繼承基類的方法,并且做出自己的改變或者擴展(代碼重用) ?

?

二:聲明某個子類兼容于某基類,定義一個接口類Interface,接口類中定義了一些接口名(就是函數名)且并未實現接口的功能,子類繼承接口類,并且實現接口中的功能

# 一:這樣不好,我要統一一下支付的規則.class QQpay:def pay(self,money):print('使用qq支付%s元' % money)class Alipay:def pay(self,money):print('使用阿里支付%s元' % money)a = Alipay()
a.pay(100)b = QQpay()
b.pay(200)# 二,統一支付的規則 歸一化設計,統一 pay接口
class QQpay:def pay(self,money):print('使用qq支付%s元' % money)class Alipay:def pay(self,money):print('使用阿里支付%s元' % money)def pay(obj,money):obj.pay(money)a = Alipay()
b = QQpay()pay(a,100)
pay(b,200)# 三,但是,來了一個野生程序員,他不知道你的約定俗成的規則,就會出問題class QQpay:def pay(self,money):print('使用qq支付%s元' % money)class Alipay:def pay(self,money):print('使用阿里支付%s元' % money)class Wechatpay:def fuqian(self,money):print('使用微信支付%s元' % money)def pay(obj,money):obj.pay(money)a = Alipay()
b = QQpay()pay(a,100)
pay(b,200)c = Wechatpay()
c.fuqian(300)# 四,解決方式
# 定義一個父類,什么都不寫,只是要求繼承我的所有類有一個pay方法,這樣就制定了一個規范,這就叫做接口類,后者抽象類.
class Payment:def pay(self):passclass QQpay(Payment):def pay(self,money):print('使用qq支付%s元' % money)class Alipay(Payment):def pay(self,money):print('使用阿里支付%s元' % money)class Wechatpay(Payment):def fuqian(self,money):print('使用微信支付%s元' % money)def pay(obj,money):obj.pay(money)a = Alipay()
b = QQpay()pay(a,100)
pay(b,200)c = Wechatpay()
c.fuqian(300)#五,他還是不知道看你這些都繼承了一個類,所以你要制定一個規范,強制他執行.
# 創建一個規范
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):    # 抽象類 接口類  規范和約束  metaclass指定的是一個元類
    @abstractmethoddef pay(self):pass  # 抽象方法class Alipay(Payment):def pay(self,money):print('使用支付寶支付了%s元'%money)class QQpay(Payment):def pay(self,money):print('使用qq支付了%s元'%money)class Wechatpay(Payment):# def pay(self,money):#     print('使用微信支付了%s元'%money)def recharge(self):passdef pay(a,money):a.pay(money)a = Alipay()
a.pay(100)
pay(a,100)    # 歸一化設計:不管是哪一個類的對象,都調用同一個函數去完成相似的功能
q = QQpay()
q.pay(100)
pay(q,100)
w = Wechatpay()
pay(w,100)   # 到用的時候才會報錯# 抽象類和接口類做的事情 :建立規范
# 制定一個類的metaclass是ABCMeta,
# 那么這個類就變成了一個抽象類(接口類)
# 這個類的主要功能就是建立一個規范

實踐中,繼承的第一種含義意義并不很大,甚至常常是有害的。因為它使得子類與基類出現強耦合。

繼承的第二種含義非常重要。它又叫“接口繼承”。
接口繼承實質上是要求“做出一個良好的抽象,這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的所有對象”——這在程序設計上,叫做歸一化。

歸一化使得高層的外部使用者可以不加區分的處理所有接口兼容的對象集合——就好象linux的泛文件概念一樣,所有東西都可以當文件處理,不必關心它是內存、磁盤、網絡還是屏幕(當然,對底層設計者,當然也可以區分出“字符設備”和“塊設備”,然后做出針對性的設計:細致到什么程度,視需求而定)。

依賴倒置原則:
高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該應該依賴細節;細節應該依賴抽象。換言之,要針對接口編程,而不是針對實現編程

在python中根本就沒有一個叫做interface的關鍵字,上面的代碼只是看起來像接口,其實并沒有起到接口的作用,子類完全可以不用去實現接口?,如果非要去模仿接口的概念,可以借助第三方模塊:

http://pypi.python.org/pypi/zope.interface

twisted的twisted\internet\interface.py里使用zope.interface

文檔https://zopeinterface.readthedocs.io/en/latest/

設計模式:https://github.com/faif/python-patterns

接口提取了一群類共同的函數,可以把接口當做一個函數的集合。然后讓子類去實現接口中的函數。這么做的意義在于歸一化,什么叫歸一化,就是只要是基于同一個接口實現的類,那么所有的這些類產生的對象在使用時,從用法上來說都一樣。歸一化,讓使用者無需關心對象的類是什么,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。比如:我們定義一個動物接口,接口里定義了有跑、吃、呼吸等接口函數,這樣老鼠的類去實現了該接口,松鼠的類也去實現了該接口,由二者
分別產生一只老鼠和一只松鼠送到你面前,即便是你分別不到底哪只是什么鼠你肯定知道他倆都會跑,都會吃,都能呼吸。再比如:我們有一個汽車接口,里面定義了汽車所有的功能,然后由本田汽車的類,奧迪汽車的類,大眾汽車的類,他們都實現了汽車接口,
這樣就好辦了,大家只需要學會了怎么開汽車,那么無論是本田,還是奧迪,還是大眾我們都會開了,開的時候根本無需關心我開的是哪一類車,
操作手法(函數調用)都一樣

抽象類

什么是抽象類

? ? 與java一樣,python也有抽象類的概念但是同樣需要借助模塊實現,抽象類是一個特殊的類,它的特殊之處在于只能被繼承,不能被實例化

為什么要有抽象類

??? 如果說類是從一堆對象中抽取相同的內容而來的,那么抽象類是從一堆中抽取相同的內容而來的,內容包括數據屬性和函數屬性。

  比如我們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要么是吃一個具體的香蕉,要么是吃一個具體的桃子。。。。。。你永遠無法吃到一個叫做水果的東西。

? ? 從設計角度去看,如果類是從現實對象抽象而來的,那么抽象類就是基于類抽象而來的。

  從實現角度來看,抽象類與普通類的不同之處在于:抽象類中有抽象方法,該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點類似,但其實是不同的,即將揭曉答案

抽象類與接口類

抽象類的本質還是類,指的是一組類的相似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的相似性。

抽象類是一個介于類和接口直接的一個概念,同時具備類和接口的部分特性,可以用來實現歸一化設計?

在python中,并沒有接口類這種東西,即便不通過專門的模塊定義接口,我們也應該有一些基本的概念。

1.多繼承問題

在繼承抽象類的過程中,我們應該盡量避免多繼承;
而在繼承接口的時候,我們反而鼓勵你來多繼承接口

接口隔離原則:
使用多個專門的接口,而不使用單一的總接口。即客戶端不應該依賴那些不需要的接口。

2.方法的實現

在抽象類中,我們可以對一些抽象方法做出基礎實現;
而在接口類中,任何方法都只是一種規范,具體的功能需要子類實現

多態

Pyhon不支持Java和C#這一類強類型語言中多態的寫法,但是原生多態,其Python崇尚“鴨子類型”。

class F1:passclass S1(F1):def show(self):print 'S1.show'class S2(F1):def show(self):print 'S2.show'# 由于在Java或C#中定義函數參數時,必須指定參數的類型
# 為了讓Func函數既可以執行S1對象的show方法,又可以執行S2對象的show方法,所以,定義了一個S1和S2類的父類
# 而實際傳入的參數是:S1對象和S2對象def Func(F1 obj):"""Func函數需要接收一個F1類型或者F1子類的類型"""print obj.show()s1_obj = S1()
Func(s1_obj) # 在Func函數中傳入S1類的對象 s1_obj,執行 S1 的show方法,結果:S1.show

s2_obj = S2()
Func(s2_obj) # 在Func函數中傳入Ss類的對象 ss_obj,執行 Ss 的show方法,結果:S2.show

Python偽代碼實現Java或C#的多態
class F1:passclass S1(F1):def show(self):print 'S1.show'class S2(F1):def show(self):print 'S2.show'def Func(obj):print obj.show()s1_obj = S1()
Func(s1_obj) s2_obj = S2()
Func(s2_obj) Python “鴨子類型”

封裝

封裝,顧名思義就是將內容封裝到某個地方,以后再去調用被封裝在某處的內容。

所以,在使用面向對象的封裝特性時,需要:

  • 將內容封裝到某處
  • 從某處調用被封裝的內容

第一步:將內容封裝到某處

self 是一個形式參數,當執行 obj1 = Foo('wupeiqi', 18 ) 時,self 等于 obj1

? ? ? ? ? ? ? ? ? ? ? ? ? ? ??當執行 obj2 = Foo('alex', 78 ) 時,self 等于 obj2

所以,內容其實被封裝到了對象 obj1 和 obj2 中,每個對象中都有 name 和 age 屬性,在內存里類似于下圖來保存。

?

第二步:從某處調用被封裝的內容

調用被封裝的內容時,有兩種情況:

  • 通過對象直接調用
  • 通過self間接調用

1、通過對象直接調用被封裝的內容

上圖展示了對象 obj1 和 obj2 在內存中保存的方式,根據保存格式可以如此調用被封裝的內容:對象.屬性名

 1 class Foo:
 2  
 3     def __init__(self, name, age):
 4         self.name = name
 5         self.age = age
 6  
 7 obj1 = Foo('wupeiqi', 18)
 8 print obj1.name    # 直接調用obj1對象的name屬性
 9 print obj1.age     # 直接調用obj1對象的age屬性
10  
11 obj2 = Foo('alex', 73)
12 print obj2.name    # 直接調用obj2對象的name屬性
13 print obj2.age     # 直接調用obj2對象的age屬性

2、通過self間接調用被封裝的內容

執行類中的方法時,需要通過self間接調用被封裝的內容

class Foo:def __init__(self, name, age):self.name = nameself.age = agedef detail(self):print self.nameprint self.ageobj1 = Foo('wupeiqi', 18)
obj1.detail()  # Python默認會將obj1傳給self參數,即:obj1.detail(obj1),所以,此時方法內部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18
  
obj2 = Foo('alex', 73)
obj2.detail()  # Python默認會將obj2傳給self參數,即:obj1.detail(obj2),所以,此時方法內部的 self = obj2,即:self.name 是 alex ; self.age 是 78

綜上所述,對于面向對象的封裝來說,其實就是使用構造方法將內容封裝到 對象 中,然后通過對象直接或者self間接獲取被封裝的內容。

?

轉載于:https://www.cnblogs.com/caisong/p/9254441.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/251430.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/251430.shtml
英文地址,請注明出處:http://en.pswp.cn/news/251430.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

部署項目的問題(三)—— node啟動服務時listen監聽的端口被占用

Error: listen EADDRINUSE :::8888表示的就是listen監聽的端口被占用 查詢什么進程占用了8888端口 sudo fuser -n tcp 8888 (指令一) 或者 netstat -tln | grep 8888 (指令二)反復執行指令一,總得到不同結果&#x…

es6 --- 內置的Symbol值

Symbol.hasInstance // Symbol.hasInstance class MyClass {[Symbol.hasInstance] (foo) {return foo instanceof Array;} } [1, 2, 3] instanceof new MyClass() // true// symbol.hasInstance:會在[1, 2, 3] instanceof 時 自動調用 [Symbol.hasInstance] (foo) 方法... //…

對象的克隆

對象的克隆 1、克隆即復制的意思,對象的克隆,意味著生成一個對象,這個對象和某個對象的屬性和行為是一致的,但是這個對象和源對象是兩個不同的對象。實現對象的克隆,方法是實現Cloneable接口,否則會報異常C…

15 調試

1. pdb pdb是基于命令行的調試工具,非常類似gnu的gdb(調試c/c)。 def getAverage(a,b):result abprint("result is %s"%result)return resulta 10 b 20 c ab ret getAverage(a,b) print(ret) 2.執行時調試 程序啟動&#xff0…

html5播放視頻只有聲音不出現畫面?

一開始網上大神們都是要把MP4的編碼格式轉換成AVC(H264),然后趕緊用格式工廠把它給換了,結果!! 沒用!!還是黑屏???咋回事啊,然后自己又…

vue項目代碼改進(一)login組件

Login登錄組件 1. 新增登錄頭像(css樣式回顧) 1)div.avatar 2)子絕父相定位,left…top… 3)border 4)placeholder 5)box-shadow box-shadow: offset-x offset-y blur spread color …

es6 --- set實現并集(Union)、交集(Intersect)和差集(Difference)

Set:類似于數組,但是成員的值都是唯一的 let a new Set([1, 2, 3]); let b new Set([4, 3, 2]);// 并集 let union new Set([...a, ...b]);// 交集 let intersect new Set([...a].filter(x > b.has(x)));// 差級 let difference new Set( [...a].filter(x > !b.has…

解析DBF文件

上周,公司給了許多DBF后綴的數據文件讓我進行解析。 因為是DBF文件我發現mysql,和Oracle都能直接對DBF文件進行導入。在導入過程中發現這些數據庫并不能識別這些文件。 通過百度找到了打開這種文件的軟件Visual FoxPro、Access,用它們打開后出…

Scrum 沖刺 第一日

Scrum 沖刺 第一日 站立式會議燃盡圖Alpha 階段認領任務明日任務安排項目預期任務量成員貢獻值計算規則今日貢獻量參考資料站立式會議 返回目錄 燃盡圖 返回目錄 Alpha 階段認領任務 學號組員分工用時20162309邢天岳補充說明書&部分測試18h20162311張之睿編寫代碼20h201623…

淺析 NodeJs 的幾種文件路徑

Node 中的文件路徑大概有 __dirname, __filename, process.cwd(), ./ 或者 ../,前三個都是絕對路徑,為了便于比較,./ 和 ../ 我們通過 path.resolve(./)來轉換為絕對路徑。 先看一個簡單的栗子: 假如我們有這樣的文件結構&#xf…

Vue項目代碼改進(二)—— element-UI的消息顯示時間修改

Message 消息提示 Options duration 顯示時間, 毫秒。設為 0 則不會自動關閉 — 默認值3000 全局重寫 element 的message 消息提示,修改時間,在main.js里 Vue.prototype.$message function (msg) {ElementUI.Message(msg) } Vue.prototype.$message.success func…

es6 --- 使用node的memoryUsage檢測WeakMap()

打開node命令行 $ node --expose-gc// --expose-gc:表示允許手動執行垃圾回收機制// 手動執行一次垃圾回收,保證獲取的內存使用狀態準確 > global.gc();// 查看內存占用的初始狀態, > process.memoryUsage();可以發現初始用了4.7MB左右 // 創建一個WeakMap()實例wm >…

遍歷字典

Python支持對字典的遍歷,有多種遍歷字典的方式:所有的鍵值對,鍵或者值。 遍歷所有的鍵值對: people {name:winter, age:25, sex:man, }for key,value in people.items():print("\nkey:"key)print("value…

Flexbox 布局

Flexbox 是 flexible box 的簡稱(愚人碼頭注:意思是“靈活的盒子容器”),是 CSS3 引入的新的布局模式。它決定了元素如何在頁面上排列,使它們能在不同的屏幕尺寸和設備下可預測地展現出來。 它之所以被稱為 Flexbox &a…

利用jQuery和bootstrap更改radio樣式

<div class"container body-content"><div class"row"><div class"text-center col-xs-12"><h3>標題</h3><div class"well well-sm"><div class"btn-group" data-toggle"butto…

將markdown編譯為HTML和PDF

使用gulp搭建markdown編譯環境 1. 執行npm init 進行項目初始化得到package.json 2. 全局安裝gulp &#xff1a;npm install gulp --global; 3. 在項目中安裝gulp依賴&#xff1a;npm install gulp --save-dev; 4. 創建gulpfile.js文件設置任務&#xff1a; var gulp require…

捕獲異常的兩種方式

捕獲異常的兩種方式方法一 #codingutf-8 import systry:with open("ddd.txt", "r") as f:data f.read()print data except:err sys.exc_info()print errsys.exc_info()返回三元組&#xff0c;分別是&#xff0c;異常類型、異常值、異常追溯地址方法二 #c…

Vue項目代碼改進(三)—— Cookie、LocalStorage和SessionStorage的使用

存在問題&#xff1a; 如果在退出頁面時&#xff0c;沒有點擊“退出”按鈕&#xff0c;而是直接關閉頁面&#xff0c;token并沒有被清除&#xff0c;依然能通過訪問http://localhost:8080/#/ 直接進入主頁。 原因&#xff1a; 使用了localStorage而非sessionStorage或Cookie 一…

es6 --- Proxy實例的get方法

寫一個攔截函數,訪問目標對象不存在屬性時,會拋出不存在該屬性的錯誤 如果存在該屬性時,就返回其值. var person {name: "張三" };var proxy new Proxy(person, {get: function(target, property) {if (property in target) {return target[property];} else {thr…

webstorm前端常用快捷鍵

Ctrl / 行注釋/取消行注釋 Ctrl Shift / 塊注釋/取消塊注釋 Ctrl W 選擇代碼塊&#xff0c;一般是增量選擇 Ctrl Shift W 上個快捷鍵的回退&#xff0c;減量選擇代碼 Alt Q 上下文信息 A…