流畅的python,Fluent Python 第六章笔记
使用一等函数实现设计模式。
6.1案例分析:重构策略模式。
根据策略模式定制一个网店的折扣模式:
1、有1000或以上积分的顾客,每个订单享5%的折扣
2、同一个订单中,单个商品的数量达到20个或以上,享受10%折扣
3、订单中的不同商品达到10个或以上,享7%折扣。
一个订单只能享受一个折扣。
根据书中的策略模式,我按照书中的方式写了代码,其实order属于上下文,它接收了所有的数据,进行统一汇总输出。
order
from collections import namedtuple
from lineitem import LineItem
from promo_all import *
Customer = namedtuple('Customer', 'name fidelity')
class Order: # 上下文
def __init__(self, customer, cart, promotion=None):
'''参数1用户模型,参数2购物车,参数3优惠方案'''
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 == 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())
if __name__ == '__main__':
joe = Customer('John', 0)
ann = Customer('Ann Smith', 11000)
cart = [
LineItem('banana', 4, .5),
LineItem('apple', 10, 1.5),
LineItem('watermellon', 5, 5)
]
joe_o = Order(joe, cart, FidelityPromo())
print(joe_o)
ann_o = Order(ann, cart, FidelityPromo())
print(ann_o)
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
from abc import ABC
from abc import abstractmethod
class Promotion(ABC):
@abstractmethod
def discount(self, order):
'''返回折扣的金额'''
from promotion import Promotion
class FidelityPromo(Promotion):
'''为积分为1000或以上的顾客的5%折扣'''
def discount(self, order):
'''order就是上下文对接本身'''
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
class BulkItemPromo(Promotion):
'''单个商品为20个或以上'''
def discount(self, order):
discount = 0
for item in order.carr():
if item.quantity >= 20:
discount += item.total * .1
return discount
class LargeOrderPromo(Promotion):
'''订单中的不同商品达到10个或以上提供的优惠'''
def discount(self, order):
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * .7
return 0
代码中用到了抽象基类。
从代码中发现,传入的优惠是一个实例,里面也只有简单的计算功能,完全可以改成函数。
from collections import namedtuple
from lineitem import LineItem
from promo_all import *
from promo_all_func import *
Customer = namedtuple('Customer', 'name fidelity')
class Order: # 上下文
def __init__(self, customer, cart, promotion=None):
'''参数1用户模型,参数2购物车,参数3优惠方案'''
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 == None:
discount = 0
else:
# discount = self.promotion.discount(self)
discount = self.promotion(self)
return self.total() - discount
def __repr__(self):
'''格式化输出实际价格以及优惠后的价格'''
fmt = '<Order total: {:.2f} due: {:.2f}>'
return fmt.format(self.total(), self.due())
if __name__ == '__main__':
joe = Customer('John', 0)
ann = Customer('Ann Smith', 11000)
cart = [
LineItem('banana', 4, .5),
LineItem('apple', 10, 1.5),
LineItem('watermellon', 5, 5)
]
joe_o = Order(joe, cart, fidelity_promo)
print(joe_o)
ann_o = Order(ann, cart, fidelity_promo)
print(ann_o)
def fidelity_promo(order):
'''order就是上下文对接本身'''
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
def bulkitem_promo(order):
discount = 0
for item in order.carr():
if item.quantity >= 20:
discount += item.total * .1
return discount
def largeorder_promo(order):
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * .7
return 0
改函数的话,order里面就在优惠的执行上面改了一下,不需要调用方法,直接()调用函数。
因为有三个不同的优惠方案,选一个合适的方法。当然也可以通过函数实现。(书中介绍了三种导入优惠函数的方法。)
第一种也是我这样的人想到的一种:
promos = [fidelity_promo, bulkitem_promo, largeorder_promo]
def best_promo(order):
'''选择最佳折扣方案'''
return max(promo(order) for promo in promos)
直接导入三个函数,循环执行函数,然后挑出优惠最大的。这个唯一的不方便就是每次都要自己手动添加函数。
第二种用过globals函数执行,取出所有的赋值对象。
promos = [globals()[name] for name in globals()
if name.endswith('_promo') and name != 'best_promo'
]
def best_promo(order):
'''选择最佳折扣方案'''
return max(promo(order) for promo in promos)
然后根据条件选择出自己所要的函数,唯一的缺点,函数的名字需要统一名称。
第三种,通过inspect的使用,导入整个模块,判断里面的模块是否是函数,和姓名等信息,输出到列表。
import promo_all_func
import inspect
# 导入模块通过inspect.isfunction来获取木块内的方法于属性,通过条件判断,
# 输出需要的函数,最方便的。
promos = [func for name,func in inspect.getmembers
(promo_all_func, inspect.isfunction) if name != 'best_promo']
print(best_promo(ann_o))
三种不同的方法,明显最后一种最方便了,如果有后续的优惠方案,直接添加进去就可以了。
over,这个章节主要讲述了把函数当做对象传递比传递实例方便的操作。
浙公网安备 33010602011771号