Python實(shí)現(xiàn)多態(tài)、協(xié)議和鴨子類型的代碼詳解
多態(tài)
問起面向?qū)ο蟮娜筇匦?,幾乎每個(gè)人都能對(duì)答如流:封裝、繼承、多態(tài)。今天我們就要來說一說 Python 中的多態(tài)。
所謂多態(tài):就是指一個(gè)類實(shí)例的相同方法在不同情形有不同表現(xiàn)形式。多態(tài)機(jī)制使具有不同內(nèi)部結(jié)構(gòu)的對(duì)象可以共享相同的外部接口。這意味著,雖然針對(duì)不同對(duì)象的具體操作不同,但通過一個(gè)公共的類,它們(那些操作)可以通過相同的方式予以調(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)鍵字,就是說,Python 里沒有像 java、C# 一樣的接口。
在策略模式一文的實(shí)現(xiàn)中,使用了抽象基類(Abstract Base Class,ABC)來實(shí)現(xiàn)接口,這主要是為了寫法上看起來和 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é)議和鴨子類型
所謂 鴨子類型 就是:如果一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么它就是鴨子。這個(gè)概念的名字來源于 James Whitcomb Riley 提出的鴨子測(cè)試。
初次看到這個(gè)描述的小伙伴一定一頭霧水,為了理解鴨子類型,我們不得不提到另一個(gè)名詞——協(xié)議。
在面向?qū)ο缶幊讨校瑓f(xié)議是非正式的接口,是一組方法,只由文檔和約定定義,因此,協(xié)議不能像正式接口那樣施加強(qiáng)制性約束。而 Python 的哲學(xué)就是盡量支持基本協(xié)議。
翻譯成人話,就是:Python 中沒有接口,在需要使用接口的地方,就用協(xié)議代替。所謂協(xié)議,其實(shí)就是一組方法,和接口中定義的方法一個(gè)意思。只不過協(xié)議是不是強(qiáng)制性的約定,如果你不遵守協(xié)議,那么也沒關(guān)系,運(yùn)行時(shí)報(bào)錯(cuò)就是了。
這樣就好理解鴨子類型了,“如果一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子” 這就表示已經(jīng)遵守了協(xié)議,“那么它就是鴨子”,意味著你可以在其他用到“鴨子”的地方,用“這只鳥”替換。這不就是多態(tà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ì)象的特征不是由父類決定,而是通過對(duì)象的方法決定的。
Python 不是不支持多態(tài),而是 Python 本身就是一門多態(tài)的語(yǔ)言。
相關(guān)文章
tf.truncated_normal與tf.random_normal的詳細(xì)用法
本篇文章主要介紹了tf.truncated_normal與tf.random_normal的詳細(xì)用法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03
python調(diào)用matplotlib模塊繪制柱狀圖
這篇文章主要為大家介紹了python調(diào)用matplotlib模塊繪制柱狀圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10
如何用VScode配置Python開發(fā)環(huán)境
這篇文章主要介紹了如何用VScode配置Python開發(fā)環(huán)境,vscode有很多優(yōu)點(diǎn),用VScode來編寫Python,也是相當(dāng)?shù)暮糜玫?需要的朋友可以參考下2023-03-03
詳談python read readline readlines的區(qū)別
下面小編就為大家?guī)硪黄斦刾ython read readline readlines的區(qū)別。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
Python?pandas修剪函數(shù)clip使用實(shí)例探究
在數(shù)據(jù)處理和分析中,經(jīng)常面臨著需要限制數(shù)據(jù)范圍的情況,而pandas庫(kù)提供的clip函數(shù)就是一個(gè)強(qiáng)大的工具,可以方便地對(duì)數(shù)據(jù)進(jìn)行修剪,本文將深入介紹clip函數(shù)的基本用法、常見參數(shù)以及實(shí)際場(chǎng)景中的應(yīng)用,以幫助大家充分理解并靈活運(yùn)用這一功能2024-01-01
pandas 缺失值與空值處理的實(shí)現(xiàn)方法
這篇文章主要介紹了pandas 缺失值與空值處理的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10

