Python實(shí)現(xiàn)多態(tài)、協(xié)議和鴨子類型的代碼詳解
多態(tài)
問(wèn)起面向?qū)ο蟮娜筇匦裕瑤缀趺總€(gè)人都能對(duì)答如流:封裝、繼承、多態(tài)。今天我們就要來(lái)說(shuō)一說(shuō) Python 中的多態(tài)。
所謂多態(tài):就是指一個(gè)類實(shí)例的相同方法在不同情形有不同表現(xiàn)形式。多態(tài)機(jī)制使具有不同內(nèi)部結(jié)構(gòu)的對(duì)象可以共享相同的外部接口。這意味著,雖然針對(duì)不同對(duì)象的具體操作不同,但通過(guò)一個(gè)公共的類,它們(那些操作)可以通過(guò)相同的方式予以調(diào)用。
我在《Python 中的設(shè)計(jì)模式詳解之:策略模式》一文中詳細(xì)描述了策略模式的實(shí)現(xiàn),而策略模式就是典型的多態(tài)應(yīng)用。
之前的代碼我就不貼了,大家可以去原文中查看。我依然還是以商品折扣的經(jīng)典舉例。策略模式一文中,傳統(tǒng)的策略模式實(shí)現(xiàn)方式我也是用 Python 代碼實(shí)現(xiàn)的,在 java 或 C# 等語(yǔ)言中,實(shí)現(xiàn)方式也差不多。以下是 C# 代碼,我只列了個(gè)架子:
interface Promotion { double discount(Order order); } class FidelityPromo : Promotion // 第一個(gè)具體策略 { // 為積分為1000或以上的顧客提供5%折扣 public double discount(Order order) { ... } } class BulkItemPromo : Promotion // 第二個(gè)具體策略 { //單個(gè)商品為20個(gè)或以上時(shí)提供10%折扣 public double discount(Order order) { ... } } class LargeOrderPromo : Promotion // 第三個(gè)具體策略 { //訂單中的不同商品達(dá)到10個(gè)或以上時(shí)提供7%折扣 public double discount(Order order) { ... } }
可以看到,首先要有一個(gè)接口(Promotion),然后各個(gè)策略去實(shí)現(xiàn)這個(gè)接口。然而,Python 語(yǔ)言沒有 interface 關(guān)鍵字,就是說(shuō),Python 里沒有像 java、C# 一樣的接口。
在策略模式一文的實(shí)現(xiàn)中,使用了抽象基類(Abstract Base Class,ABC)來(lái)實(shí)現(xiàn)接口,這主要是為了寫法上看起來(lái)和 java、C# 等語(yǔ)言更加的像,易于有這些語(yǔ)言基礎(chǔ)的同學(xué)理解和對(duì)比。
抽象基類是在 Python 語(yǔ)言誕生 15 年后,Python 2.6 才引入的。這里我們不詳細(xì)介紹抽象基類,因?yàn)榧幢悻F(xiàn)在也很少有代碼使用抽象基類。對(duì)于多態(tài),Python 有更好的實(shí)現(xiàn)方式——鴨子類型(duck typing)。
協(xié)議和鴨子類型
所謂 鴨子類型 就是:如果一只鳥走起來(lái)像鴨子、游泳起來(lái)像鴨子、叫起來(lái)也像鴨子,那么它就是鴨子。這個(gè)概念的名字來(lái)源于 James Whitcomb Riley 提出的鴨子測(cè)試。
初次看到這個(gè)描述的小伙伴一定一頭霧水,為了理解鴨子類型,我們不得不提到另一個(gè)名詞——協(xié)議。
在面向?qū)ο缶幊讨?,協(xié)議是非正式的接口,是一組方法,只由文檔和約定定義,因此,協(xié)議不能像正式接口那樣施加強(qiáng)制性約束。而 Python 的哲學(xué)就是盡量支持基本協(xié)議。
翻譯成人話,就是:Python 中沒有接口,在需要使用接口的地方,就用協(xié)議代替。所謂協(xié)議,其實(shí)就是一組方法,和接口中定義的方法一個(gè)意思。只不過(guò)協(xié)議是不是強(qiáng)制性的約定,如果你不遵守協(xié)議,那么也沒關(guān)系,運(yùn)行時(shí)報(bào)錯(cuò)就是了。
這樣就好理解鴨子類型了,“如果一只鳥走起來(lái)像鴨子、游泳起來(lái)像鴨子、叫起來(lái)也像鴨子” 這就表示已經(jīng)遵守了協(xié)議,“那么它就是鴨子”,意味著你可以在其他用到“鴨子”的地方,用“這只鳥”替換。這不就是多態(tài)嗎?
用“鴨子類型”來(lái)實(shí)現(xiàn)策略模式也很簡(jiǎn)單,刪掉抽象基類就可以了。(這就是為什么抽象基類很少使用的原因,因?yàn)閯h掉代碼也一樣正確啊。)有興趣的小伙伴可以自己嘗試一下代碼。
Python 中的協(xié)議舉例
Python 中有很多的協(xié)議,比如迭代器協(xié)議,任何實(shí)現(xiàn)了 __iter__ 和 __next__ 方法的對(duì)象都可稱之為迭代器,但對(duì)象本身是什么類型不受限制,這得益于鴨子類型。
from collections import Iterable from collections import Iterator class MyIterator: def __iter__(self): pass def __next__(self): pass print(isinstance(MyIterator(), Iterable)) print(isinstance(MyIterator(), Iterator))
輸出:
True
True
結(jié)語(yǔ)
鴨子類型是編程語(yǔ)言中動(dòng)態(tài)類型語(yǔ)言中的一種設(shè)計(jì)風(fēng)格,一個(gè)對(duì)象的特征不是由父類決定,而是通過(guò)對(duì)象的方法決定的。
Python 不是不支持多態(tài),而是 Python 本身就是一門多態(tài)的語(yǔ)言。
相關(guān)文章
selenium+python 去除啟動(dòng)的黑色cmd窗口方法
今天小編就為大家分享一篇selenium+python 去除啟動(dòng)的黑色cmd窗口方法。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Pyside6 安裝和簡(jiǎn)單界面開發(fā)過(guò)程詳細(xì)介紹
PySide是跨平臺(tái)應(yīng)用程序框架Qt的Python綁定,Qt是跨平臺(tái)C++圖形可視化界面應(yīng)用開發(fā)框架,自推出以來(lái)深受業(yè)界盛贊,Pyside6是利用Python語(yǔ)言進(jìn)行開發(fā)的GUI,所以在使用Pyside6前要先安裝Python環(huán)境,本文給大家介紹Pyside6 安裝和簡(jiǎn)單界面開發(fā)過(guò)程,一起看看吧2023-10-10Django在Win7下的安裝及創(chuàng)建項(xiàng)目hello word簡(jiǎn)明教程
這篇文章主要介紹了Django在Win7下的安裝及創(chuàng)建項(xiàng)目hello word,需要的朋友可以參考下2014-07-07解決Ubuntu pip 安裝 mysql-python包出錯(cuò)的問(wèn)題
今天小編就為大家分享一篇解決Ubuntu pip 安裝 mysql-python包出錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06解決python3運(yùn)行selenium下HTMLTestRunner報(bào)錯(cuò)的問(wèn)題
今天小編就為大家分享一篇解決python3運(yùn)行selenium下HTMLTestRunner報(bào)錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12使用Python對(duì)零售商品進(jìn)行數(shù)據(jù)分析
這篇文章主要為大家介紹了使用Python對(duì)零售商品進(jìn)行數(shù)據(jù)分析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05