一、核心要义
《设计模式:可复用面向对象软件的基础》一书中有23个设计模式,其中有16个在动态语言中"不见了或者简化了"。作为动态语言之一的Python, 我们可以利用一等函数简化其中的某些设计模式,本章主要介绍如何使用一等函数重构”策略“模式(关于何为策略模式,可参考策略设计模式)。
二、案例基本描述
用户购置商品,根据不同条件(用户会员积分、单类商品数量、购买的不同商品种类数量),享有不同的优惠策略,最后结算扣除优惠后应该支付的金额。
实体包括:
- 用户:使用具名元组,包含名字和会员积分两个属性
- 单类商品订单:使用类,包含商品名称、商品数量和单价和三个属性
- 优惠策略
(1)如果用户积分大于1000,享受5%优惠
(2)单类商品为20个及以上时,享受10%折扣
(3)购买的不同商品种类数量达到3个或以上,享受7%折扣
二、代码示例
1、案例涉及前置知识
(1)globals
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/1/27 12:31
# @Author : Maple
# @File : 00-相关背景知识.py
# @Software: PyCharmdef fun1(a,b):return a + bclass Person:def __init__(self,name,age):self.name = nameself.age = agedef reading(self):passRATIO = 1.2if __name__ == '__main__':g = globals()# 获取当前模块的相关信息(包名,模块路径,模块包含的函数,类等)print(g) # {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000018405083DC0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/01-study/python/fluent_python/06-设计模式/00-相关背景知识.py', '__cached__': None, 'fun1': <function fun1 at 0x000001840543D1F0>, 'Person': <class '__main__.Person'>, 'RATIO': 1.2, 'g': {...}}
(2)获取当前模块所包含的所有函数
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/1/22 22:16
# @Author : Maple
# @File : promotions.py
# @Software: PyCharmimport inspect
import promotions"""
获取当前模块包含的所有函数
"""def f1():passdef f2():passif __name__ == '__main__':print(promotions) # <module 'promotions' from 'D:\\01-study\\python\\fluent_python\\06-设计模式\\promotions.py'>pros = [func for name,func in inspect.getmembers(promotions,inspect.isfunction)]print(pros) # [<function f1 at 0x00000217F33E5C10>, <function f2 at 0x00000217F36539D0>]
2、策略模式传统写法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/1/27 9:48
# @Author : Maple
# @File : 01-策略模式传统写法.py
# @Software: PyCharm
from abc import ABC, abstractmethod
from collections import namedtuple# 顾客具名元组
Customer = namedtuple('Customer','name fidelity')# # 定义商品类
class Item:def __init__(self,product,quantity,price):""":param product: 商品名称:param quantity: 商品数量:param price: 商品单价"""self.product = productself.quantity = quantityself.price = pricedef totol(self):""":return:订单总费用"""return self.quantity * self.price# 定义上下文
class Order:def __init__(self,customer,cart,promotion=None):""":param customer: 顾客:param cart: 购物车:param promotion: 优惠策略"""self.customer = customerself.cart = cartself.promotion = promotiondef total(self):""":return:顾客订单打折之前的总费用"""if not hasattr(self,'__total'):self.__total = sum(item.totol() for item in self.cart)return self.__totaldef due(self):""":return:顾客最终支付的费用"""if self.promotion is None:return self.total()return self.total() - self.promotion.discount(self)def __repr__(self):fmt = '<Order total: {:.2f} due: {:.2f}>'return fmt.format(self.total(),self.due())# 定义优惠策略的基类
class BasePromotion(ABC):@abstractmethoddef discount(self,order):"""返回折扣金额"""# 具体策略1:积分优惠策略
class FidelityPromo(BasePromotion):"""如果积分大于1000,享受5%优惠"""def discount(self,order):if order.customer.fidelity > 1000:return order.total() * 0.05else:return 0# 具体策略2
class BulkItempromo(BasePromotion):"""单个商品为20个及以上时,提供10%折扣"""def discount(self,order):discount = 0for item in order.cart:if item.quantity >= 10:discount += item.totol()* 0.1return discount# 具体策略3
class LargetOrderPromo(BasePromotion):"""购物车中不同商品种类数量达到3个或以上提供7%折扣"""def discount(self,order):discount = 0# 获取购物车中所有不同的商品products = {item.product for item in order.cart}if len(products) >=3:discount += order.total() * 0.07return discountif __name__ == '__main__':#1. 策略1示例cus1 = Customer('Maple',2000) # 用户积分大于1000,享受5%优惠cart1=[Item('banana',20,2.0),Item('apple',10,1.0)]p1 = FidelityPromo()o1 = Order(cus1,cart1,p1)print(o1) # <Order total: 50.00 due: 47.50> 47.5 = (40 + 10 - 50 *0.05)# 2. 策略2示例cus2 = Customer('Jack',880)cart2= [Item('Book',30,1.0),Item('Radio',5,1.0)] # Book订单超过20个,提供10%折扣p2 = BulkItempromo()o2 = Order(cus2,cart2,p2)print(o2) # <Order total: 35.00 due: 32.00> 32.00 = (30 -30 * 0.1) + 5# 策略3示例cus3 = Customer('Mick', 500)cart3 = [Item('Phone', 5, 2.0), Item('Water', 5, 1.0),Item('ring',20,2)] #购物车不同商品达到3个.提供7%折扣p3 = LargetOrderPromo()o3 = Order(cus3, cart3, p3)print(o3) # <Order total: 55.00 due: 51.15> 51.15> = (40 + 10 - 50 *0.05)
3、策略模式函数写法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/1/27 11:25
# @Author : Maple
# @File : 02-策略模式函数写法.py
# @Software: PyCharm#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/1/27 9:48
# @Author : Maple
# @File : 01-策略模式传统写法.py
# @Software: PyCharm
from abc import ABC, abstractmethod
from collections import namedtuple# 顾客具名元组
Customer = namedtuple('Customer','name fidelity')# 定义商品类
class Item:def __init__(self,product,quantity,price):""":param product: 商品名称:param quantity: 商品数量:param price: 商品单价"""self.product = productself.quantity = quantityself.price = pricedef totol(self):""":return:订单总费用"""return self.quantity * self.price# 定义上下文(订单类)
class Order:def __init__(self,customer,cart,promotion=None):""":param customer: 顾客:param cart: 购物车:param promotion: 优惠策略"""self.customer = customerself.cart = cartself.promotion = promotiondef total(self):""":return:顾客订单打折之前的总费用"""if not hasattr(self,'__total'):self.__total = sum(item.totol() for item in self.cart)return self.__totaldef due(self):""":return:顾客最终支付的费用"""if self.promotion is None:return self.total()return self.total() - self.promotion(self)def __repr__(self):fmt = '<Order total: {:.2f} due: {:.2f}>'return fmt.format(self.total(), self.due())# 具体策略1:积分优惠策略
def FidelityPromo(order):"""如果积分大于1000,享受5%优惠"""if order.customer.fidelity > 1000:return order.total() * 0.05else:return 0# 具体策略2
def BulkItempromo(order):"""单个商品为20个及以上时,提供10%折扣"""discount = 0for item in order.cart:if item.quantity >= 10:discount += item.totol()* 0.1return discount# 具体策略3
def LargetOrderPromo(order):"""购物车中不同商品种类数量达到3个或以上提供7%折扣"""discount = 0# 获取购物车中所有不同的商品products = {item.product for item in order.cart}if len(products) >=3:discount += order.total() * 0.07return discountif __name__ == '__main__':#1. 策略1示例cus1 = Customer('Maple',2000)# 用户积分大于1000,享受5%优惠cart1=[Item('banana',20,2.0),Item('apple',10,1.0)]o1 = Order(cus1,cart1,FidelityPromo)print(o1) # <Order total: 50.00 due: 47.50> 47.5 = (40 + 10 - 50 *0.05)# 2. 策略2示例cus2 = Customer('Jack',880)cart2= [Item('Book',30,1.0),Item('Radio',5,1.0)] #Book订单超过20个,提供10%折扣o2 = Order(cus2,cart2,BulkItempromo)print(o2) # <Order total: 35.00 due: 32.00> 32.00 = (30 -30 * 0.1) + 5# 策略3示例cus3 = Customer('Mick', 500)cart3 = [Item('Phone', 5, 2.0), Item('Water', 5, 1.0),Item('ring',20,2)] #购物车不同商品达到3个.提供7%折扣o3 = Order(cus3, cart3, LargetOrderPromo)print(o3) # <Order total: 55.00 due: 51.15> 51.15> = (40 + 10 - 50 *0.05)
4、策略模式函数写法-最优策略
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/1/27 11:34
# @Author : Maple
# @File : 03-策略模式函数写法-最优策略.py
# @Software: PyCharm"""
如何自动选择最优策略?
"""#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/1/27 11:25
# @Author : Maple
# @File : 02-策略模式函数写法.py
# @Software: PyCharm#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/1/27 9:48
# @Author : Maple
# @File : 01-策略模式传统写法.py
# @Software: PyCharm
from abc import ABC, abstractmethod
from collections import namedtuple# 顾客具名元组
Customer = namedtuple('Customer','name fidelity')# 定义商品类
class Item:def __init__(self,product,quantity,price):""":param product: 商品名称:param quantity: 商品数量:param price: 商品单价"""self.product = productself.quantity = quantityself.price = pricedef totol(self):""":return:订单总费用"""return self.quantity * self.price# 定义上下文(订单类)
class Order:def __init__(self,customer,cart,promotion=None):""":param customer: 顾客:param cart: 购物车:param promotion: 优惠策略"""self.customer = customerself.cart = cartself.promotion = promotiondef total(self):""":return:顾客订单打折之前的总费用"""if not hasattr(self,'__total'):self.__total = sum(item.totol() for item in self.cart)return self.__totaldef due(self):""":return:顾客最终支付的费用"""if self.promotion is None:return self.total()return self.total() - self.promotion(self)def __repr__(self):fmt = '<Order total: {:.2f} due: {:.2f}>'return fmt.format(self.total(), self.due())# 具体策略1:积分优惠策略
def FidelityPromo(order):"""如果积分大于1000,享受15%优惠"""if order.customer.fidelity > 1000:return order.total() * 0.15else:return 0# 具体策略2
def BulkItemPromo(order):"""单个商品为20个及以上时,提供10%折扣"""discount = 0for item in order.cart:if item.quantity >= 10:discount += item.totol()* 0.1return discount# 具体策略3
def LargetOrderPromo(order):"""购物车中不同商品种类数量达到3个或以上提供7%折扣"""discount = 0# 获取购物车中所有不同的商品products = {item.product for item in order.cart}if len(products) >=3:discount += order.total() * 0.07return round(discount,2)# 最优策略
def optimization_strategy(order):""":param order: 订单类:return:最优策略和最大折扣"""# 手动定义所有优惠策略promos = [FidelityPromo,BulkItemPromo,LargetOrderPromo]p_final = Nonediscount_final = 0for p in promos:discount = p(order)if discount > discount_final:discount_final = discountp_final = preturn (p_final,discount_final)# 最优策略-获取当前模块所有策略的另外一种方式
def optimization_strategy2(order):# globals返回一个字典,表示当前的全局符号表promos = [globals()[name] for name in globals()if name.endswith('Promo')]p_final = Nonediscount_final = 0for p in promos:discount = p(order)if discount > discount_final:discount_final = discountp_final = preturn (p_final, discount_final)if __name__ == '__main__':#1. 最优策略示例1cus1 = Customer('Maple',2000)# 用户积分大于1000,享受15%(注意:为了测试,数值从5%调整到15%)优惠cart1=[Item('banana',20,2.0),Item('apple',10,1.0)]o1 = Order(cus1,cart1,FidelityPromo)print(optimization_strategy(o1)) # (<function FidelityPromo at 0x000001C46A4D8DC0>, 7.5)print(optimization_strategy2(o1)) # (<function FidelityPromo at 0x000001C46A4D8DC0>, 7.5)print('=====================================================')# 2. 最优策略示例2cus2 = Customer('Jack',880)cart2= [Item('Book',30,1.0),Item('Radio',5,1.0)] #Book订单超过20个,提供10%折扣o2 = Order(cus2,cart2,BulkItemPromo)print(optimization_strategy(o2)) # (<function BulkItemPromo at 0x0000018BF0F041F0>, 3.0)print(optimization_strategy2(o2)) # (<function BulkItemPromo at 0x00000233DF99E1F0>, 3.0)print('=====================================================')# 3. 最优策略示例3cus3 = Customer('Mick', 300)cart3 = [Item('Phone', 5, 2.0), Item('Water', 5, 1.0),Item('ring',8,2)] #购物车不同商品达到3个.提供7%折扣o3 = Order(cus3, cart3, LargetOrderPromo)print(optimization_strategy(o3)) # (<function LargetOrderPromo at 0x0000024CEE094280>, 2.17)print(optimization_strategy2(o3)) # (<function LargetOrderPromo at 0x00000233DF99E280>, 2.17)