
Python設(shè)計模式之策略模式
設(shè)計模式是我們實際應(yīng)用開發(fā)中必不可缺的,對設(shè)計模式的理解有助于我們寫出可讀性和擴展更高的應(yīng)用程序。雖然設(shè)計模式與語言無關(guān),但并不意味著每一個模式都能在任何語言中使用,所以有必要去針對語言的特性去做了解。設(shè)計模式特別是對于java語言而言,已經(jīng)有過非常多的大牛寫過,所以這里我就不重復(fù)了。對于Python來說就相對要少很多,特別是python語言具有很多高級的特性,而不需要了解這些照樣能滿足開發(fā)中的很多需求,所以很多人往往忽視了這些,這里我們來在Pythonic中來感受一下設(shè)計模式。
1.介紹
策略模式也是常見的設(shè)計模式之一,它是指對一系列的算法定義,并將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法獨立于使用它的客戶而獨立變化。
這是比較官方的說法,看著明顯的一股比較抽象的感覺,通俗來講就是針對一個問題而定義出一個解決的模板,這個模板就是具體的策略,每個策略都是按照這個模板來的。這種情況下我們有新的策略時就可以直接按照模板來寫,而不會影響之前已經(jīng)定義好的策略。
2.具體實例
這里我用的《流暢的Python》中的實例,剛好雙11過去不久,相信許多小伙伴也是掏空了腰包,哈哈。那這里就以電商領(lǐng)域的根據(jù)客戶的屬性或訂單中的商品數(shù)量來計算折扣的方式來進行講解,首先來看看下面這張圖。
通過這張圖,相信能對策略模式的流程有個比較清晰的了解了。然后看看具體的實現(xiàn)過程,首先我們用namedtuple來定義一個Customer,雖然這里是說設(shè)計模式,考慮到有些小伙伴可能對Python中的具名元組不太熟悉,所以這里也簡單的說下。
namedtuple用來構(gòu)建一個帶字段名的元組和一個有名字的類,這樣說可能還是有些抽象,這里來看看下面的代碼
from collections import namedtuple
City = namedtuple('City','name country provinces')
這里測試就直接如下
changsha = City('Changsha','China','Hunan')
print(changsha)
結(jié)果如下
City(name='Changsha', country='China', province='Hunan')
還可以直接調(diào)用字段名
print(changsha.name)
更多用法可以去看看官方文檔,這里重點還是講設(shè)計模式。
好了,先來看看用類實現(xiàn)的策略模式
# 策略設(shè)計模式實例
from abc import ABC, abstractmethod
from collections import namedtuple
# 創(chuàng)建一個具名元組
Customer = namedtuple('Customer', 'name fidelity')
class LineItem:
def __init__(self, product, quantity, price):
self.product = product
self.quantity = quantity
self.price = price
def total(self):
return self.price * self.quantity
# 上下文
class Order:
?。?傳入三個參數(shù),分別是消費者,購物清單,促銷方式
def __init__(self, customer, cart, promotion=None):
self.customer = customer
self.cart = list(cart)
self.promotion = promotion
def total(self):
if not hasattr(self, '__total'):
self.__total = sum(item.total() for item in self.cart)
return self.__total
def due(self):
if self.promotion is None:
discount = 0
else:
discount = self.promotion.discount(self)
return self.total() - discount
# 輸出具體信息
def __repr__(self):
fmt = '<Order total: {:.2f} due: {:.2f}>'
return fmt.format(self.total(), self.due())
# 策略 抽象基類
class Promotion(ABC):
@abstractmethod
def discount(self, order):
"""
:param order:
:return: 返回折扣金額(正值)
"""
# 第一個具體策略
class FidelityPromo(Promotion):
""" 為積分為1000或以上的顧客提供5%的折扣 """
def discount(self, order):
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
# 第二個具體策略
class BulkItemPromo(Promotion):
""" 單個商品為20個或以上時提供10%折扣"""
def discount(self, order):
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount = item.total() * .1
return discount
# 第三個具體策略
class LargeOrderPromo(Promotion):
""" 訂單中的不同商品達到10個或以上時提供%7的折扣"""
def discount(self, order):
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * .07
return 0
這里是用類對象來實現(xiàn)的策略模式,每個具體策略類(折扣方式)都繼承了Promotion這個基類,因為discount()是一個抽象函數(shù),所以繼承Promotion的子類都需要重寫discount()函數(shù)(也就是進行具體的打折信息的函數(shù)),這樣一來,就很好的實現(xiàn)對象之間的解耦。這里的折扣方式有兩類,一類是根據(jù)用戶的積分,一類是根據(jù)用戶所購買商品的數(shù)量。具體的折扣信息也都在代碼塊里面注釋了,這里就不重復(fù)了,接下來我們來看看具體的測試用例
joe = Customer('John Doe', 0)
ann = Customer('Ann Smith', 1100)
cart = [LineItem('banana', 4, .5),
LineItem('apple', 10, 1.5),
LineItem('watermellon', 5, 5.0)]
print('John: ', Order(joe, cart, FidelityPromo()))
print('Ann: ', Order(ann, cart, FidelityPromo()))
這里定義了兩消費者,John初始積分為0,Ann初始積分為1100,然后商品購買了4個香蕉,10個蘋果,5個西瓜...說的都要流口水了,哈哈哈。回到正題,輸出時采用第一種折扣方式,Run一下
John: <Order total: 42.00 due: 42.00>
Ann: <Order total: 42.00 due: 39.90>
3.優(yōu)化措施
?類變函數(shù)
上面的策略模式是使用的類對象實現(xiàn)的,其實我們還可以用函數(shù)對象的方法實現(xiàn),看看具體的代碼
# 策略設(shè)計模式實例
from collections import namedtuple
# 創(chuàng)建一個具名元組
Customer = namedtuple('Customer', 'name fidelity')
class LineItem:
def __init__(self, product, quantity, price):
self.product = product
self.quantity = quantity
self.price = price
def total(self):
return self.price * self.quantity
# 上下文
class Order:
def __init__(self, customer, cart, promotion=None):
self.customer = customer
self.cart = list(cart)
self.promotion = promotion
def total(self):
if not hasattr(self, '__total'):
self.__total = sum(item.total() for item in self.cart)
return self.__total
def due(self):
if self.promotion is None:
discount = 0
else:
discount = self.promotion.discount(self)
return self.total() - discount
def __repr__(self):
fmt = '<Order total: {:.2f} due: {:.2f}>'
return fmt.format(self.total(), self.due())
# 第一個具體策略
def fidelity_promo(order):
""" 為積分為1000或以上的顧客提供5%的折扣 """
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
# 第二個具體策略
def bulk_item_promo(order):
""" 單個商品為20個或以上時提供10%折扣"""
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount = item.total() * .1
return discount
# 第三個具體策略
def large_order_promo(order):
""" 訂單中的不同商品達到10個或以上時提供%7的折扣"""
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * .07
return 0
這種方式?jīng)]有了抽象類,并且每個策略都是函數(shù),實現(xiàn)同樣的功能,代碼量更加少,并且測試的時候可以直接把促銷函數(shù)作為參數(shù)傳入,這里就不多說了。
?選擇最佳策略
細心的朋友可能觀察到,我們這樣每次對商品進行打折處理時,都需要自己選擇折扣方式,這樣數(shù)量多了就會非常的麻煩,那么有沒有辦法讓系統(tǒng)幫我們自動選擇呢?當(dāng)然是有的,這里我們可以定義一個數(shù)組,把折扣策略的函數(shù)當(dāng)作元素傳進去。
promos = [fidelity_promo,bulk_item_promo,large_order_promo]
然后定義一個函數(shù)
def best_promo(order):
""" 選擇可用的最佳折扣 """
return max(promo(order) for promo in promos)
這樣一來就省了很多時間,系統(tǒng)幫我們自動選擇。但是仍然有一個問題,這個數(shù)組的元素需要我們手動輸入,雖然工作量小,但是對于有強迫癥的猿來說,依然是不行的,能用自動化的方式就不要用手動,所以繼續(xù)做優(yōu)化。
promos = [globals()[name] for name in globals()
if name.endswith('_promo')
and name != 'best_promo']
這里使用了globals()函數(shù),我們就是使用這個函數(shù)來進行全局查找以’_promo’結(jié)尾的函數(shù),并且過濾掉best_promo函數(shù),又一次完成了我們的自動化優(yōu)化。
最后,這篇blog就到這里了,相信你我都更加了解Python中的策略模式了,這里我推薦對Python感興趣的朋友去看一下《Fluent Python》這本書,里面講述了很多的高級特性, 更加讓我們體驗到Python中的美學(xué)。
數(shù)據(jù)分析咨詢請掃描二維碼
若不方便掃碼,搜微信號:CDAshujufenxi
SQL Server 中 CONVERT 函數(shù)的日期轉(zhuǎn)換:從基礎(chǔ)用法到實戰(zhàn)優(yōu)化 在 SQL Server 的數(shù)據(jù)處理中,日期格式轉(zhuǎn)換是高頻需求 —— 無論 ...
2025-09-18MySQL 大表拆分與關(guān)聯(lián)查詢效率:打破 “拆分必慢” 的認知誤區(qū) 在 MySQL 數(shù)據(jù)庫管理中,“大表” 始終是性能優(yōu)化繞不開的話題。 ...
2025-09-18CDA 數(shù)據(jù)分析師:表結(jié)構(gòu)數(shù)據(jù) “獲取 - 加工 - 使用” 全流程的賦能者 表結(jié)構(gòu)數(shù)據(jù)(如數(shù)據(jù)庫表、Excel 表、CSV 文件)是企業(yè)數(shù)字 ...
2025-09-18DSGE 模型中的 Et:理性預(yù)期算子的內(nèi)涵、作用與應(yīng)用解析 動態(tài)隨機一般均衡(Dynamic Stochastic General Equilibrium, DSGE)模 ...
2025-09-17Python 提取 TIF 中地名的完整指南 一、先明確:TIF 中的地名有哪兩種存在形式? 在開始提取前,需先判斷 TIF 文件的類型 —— ...
2025-09-17CDA 數(shù)據(jù)分析師:解鎖表結(jié)構(gòu)數(shù)據(jù)特征價值的專業(yè)核心 表結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 規(guī)范存儲的結(jié)構(gòu)化數(shù)據(jù),如數(shù)據(jù)庫表、Excel 表、 ...
2025-09-17Excel 導(dǎo)入數(shù)據(jù)含缺失值?詳解 dropna 函數(shù)的功能與實戰(zhàn)應(yīng)用 在用 Python(如 pandas 庫)處理 Excel 數(shù)據(jù)時,“缺失值” 是高頻 ...
2025-09-16深入解析卡方檢驗與 t 檢驗:差異、適用場景與實踐應(yīng)用 在數(shù)據(jù)分析與統(tǒng)計學(xué)領(lǐng)域,假設(shè)檢驗是驗證研究假設(shè)、判斷數(shù)據(jù)差異是否 “ ...
2025-09-16CDA 數(shù)據(jù)分析師:掌控表格結(jié)構(gòu)數(shù)據(jù)全功能周期的專業(yè)操盤手 表格結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 存儲的結(jié)構(gòu)化數(shù)據(jù),如 Excel 表、數(shù)據(jù) ...
2025-09-16MySQL 執(zhí)行計劃中 rows 數(shù)量的準(zhǔn)確性解析:原理、影響因素與優(yōu)化 在 MySQL SQL 調(diào)優(yōu)中,EXPLAIN執(zhí)行計劃是核心工具,而其中的row ...
2025-09-15解析 Python 中 Response 對象的 text 與 content:區(qū)別、場景與實踐指南 在 Python 進行 HTTP 網(wǎng)絡(luò)請求開發(fā)時(如使用requests ...
2025-09-15CDA 數(shù)據(jù)分析師:激活表格結(jié)構(gòu)數(shù)據(jù)價值的核心操盤手 表格結(jié)構(gòu)數(shù)據(jù)(如 Excel 表格、數(shù)據(jù)庫表)是企業(yè)最基礎(chǔ)、最核心的數(shù)據(jù)形態(tài) ...
2025-09-15Python HTTP 請求工具對比:urllib.request 與 requests 的核心差異與選擇指南 在 Python 處理 HTTP 請求(如接口調(diào)用、數(shù)據(jù)爬取 ...
2025-09-12解決 pd.read_csv 讀取長浮點數(shù)據(jù)的科學(xué)計數(shù)法問題 為幫助 Python 數(shù)據(jù)從業(yè)者解決pd.read_csv讀取長浮點數(shù)據(jù)時的科學(xué)計數(shù)法問題 ...
2025-09-12CDA 數(shù)據(jù)分析師:業(yè)務(wù)數(shù)據(jù)分析步驟的落地者與價值優(yōu)化者 業(yè)務(wù)數(shù)據(jù)分析是企業(yè)解決日常運營問題、提升執(zhí)行效率的核心手段,其價值 ...
2025-09-12用 SQL 驗證業(yè)務(wù)邏輯:從規(guī)則拆解到數(shù)據(jù)把關(guān)的實戰(zhàn)指南 在業(yè)務(wù)系統(tǒng)落地過程中,“業(yè)務(wù)邏輯” 是連接 “需求設(shè)計” 與 “用戶體驗 ...
2025-09-11塔吉特百貨孕婦營銷案例:數(shù)據(jù)驅(qū)動下的精準(zhǔn)零售革命與啟示 在零售行業(yè) “流量紅利見頂” 的當(dāng)下,精準(zhǔn)營銷成為企業(yè)突圍的核心方 ...
2025-09-11CDA 數(shù)據(jù)分析師與戰(zhàn)略 / 業(yè)務(wù)數(shù)據(jù)分析:概念辨析與協(xié)同價值 在數(shù)據(jù)驅(qū)動決策的體系中,“戰(zhàn)略數(shù)據(jù)分析”“業(yè)務(wù)數(shù)據(jù)分析” 是企業(yè) ...
2025-09-11Excel 數(shù)據(jù)聚類分析:從操作實踐到業(yè)務(wù)價值挖掘 在數(shù)據(jù)分析場景中,聚類分析作為 “無監(jiān)督分組” 的核心工具,能從雜亂數(shù)據(jù)中挖 ...
2025-09-10統(tǒng)計模型的核心目的:從數(shù)據(jù)解讀到?jīng)Q策支撐的價值導(dǎo)向 統(tǒng)計模型作為數(shù)據(jù)分析的核心工具,并非簡單的 “公式堆砌”,而是圍繞特定 ...
2025-09-10