Python 使用一等函数实现设计模式

案例分析:重构“策略”模式

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica }

  如果合理利用作为一等对象的函数,某些设计模式可以简化,“策略”模式就是其中一个很好的例子。

经典的“策略”模式

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica }

使用“策略”设计模式处理订单折扣的 UML 类图

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica }

电商领域有个功能明显可以使用“策略”模式,即根据客户的属性或订单中的商品计算折扣。

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica }

假如一个网店制定了下述折扣规则:

  • 有1000或者以上积分的客户,每个订单享5%折扣
  • 同一个订单中,单个商品的数量达到20个或以上,享10%折扣
  • 订单中的不同商品达到10个或以上的,享7%的折扣

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica }

简单起见,我们假定一个订单一次只能享用一个折扣。

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica }

上下文

  把一些计算委托给实现不同算法的可交互组件,它提供服务。在这个点上实例中,上下文是Order,它会根据不同的算法计算促销折扣

策略

  实现不同算法的组件共同的接口,在这个实例中,名为Promotion的抽象类扮演这个角色

具体策略

  “策略”的具体子类。fidelityPromo、BulkPromo和LargeOrderPromo是这里实现的三个具体策略

  1 from abc import ABC, abstractmethod
  2 from collections import namedtuple
  3
  4
  5 Customer = namedtuple(‘Customer‘, ‘name fidelity‘)
  6
  7
  8 class LineItem:
  9
 10     def __init__(self, product, quantity, price):
 11         self.product = product
 12         self.quantity = quantity
 13         self.price = price
 14
 15     def total(self):
 16         return self.price * self.quantity
 17
 18
 19 class Order:
 20
 21     def __init__(self, customer, cart, promotion=None):
 22         self.customer = customer
 23         self.cart = list(cart)
 24         self.promotion = promotion
 25
 26     def total(self):
 27         if not hasattr(self, ‘__total‘):
 28             self.__total = sum(item.total() for item in self.cart)
 29         return self.__total
 30
 31     def due(self):
 32         if self.promotion is None:
 33             discount = 0
 34         else:
 35             discount = self.promotion.discount(self)
 36         return self.total() - discount
 37
 38     def __repr__(self):
 39         fmt = ‘<Order total: {:.2f} due: {:.2f}>‘
 40         return fmt.format(self.total(), self.due())
 41
 42 class Promotion(ABC):
 43
 44     @abstractmethod
 45     def discount(self, order):
 46         """返回折扣金额(正值)"""
 47
 48
 49 class FidelityPromo(Promotion):# 第一个具体策略
 50     """为积分为1000货以上的顾客提供5%的折扣"""
 51
 52     def discount(self, order):
 53         return order.total() * .05 if order.customer.fidelity >= 1000 else 0
 54
 55
 56 class BulkItemPromo(Promotion): # 第二个具体策略
 57     """单个商品为20个或以上时提供10%折扣"""
 58
 59     def discount(self, order):
 60         discount = 0
 61         for item in order.cart:
 62             if item.quantity >= 20:
 63                 discount += item.total() * .1
 64         return discount
 65
 66
 67 class LargeOrderPromo(Promotion): # 第三个具体策略
 68     """订单中的不同商品达到10个或以上时提供7%折扣"""
 69
 70     def discount(self, order):
 71         distinct_items = {item.product for item in order.cart}
 72         if len(distinct_items) >= 10:
 73             return order.total() * .07
 74         return 0
 75
 76
 77 #两个顾客:joe 的积分是 0,ann 的积分是 1100
 78 joe = Customer(‘John Doe‘, 0)
 79 ann = Customer(‘Ann Smith‘, 1100)
 80
 81 #有三个商品的购物车
 82 cart = [LineItem(‘banana‘, 4, .5),
 83         LineItem(‘apple‘, 10, 1.5),
 84         LineItem(‘watermellon‘, 5, 5.0)
 85 ]
 86
 87 #fidelityPromo 没给 joe 提供折扣
 88 print(‘joe 0积分:‘, Order(joe, cart, FidelityPromo()))
 89 #ann 得到了 5% 折扣,因为她的积分超过 1000
 90 print(‘ann 1000积分:‘, Order(ann, cart, FidelityPromo()))
 91
 92 #banana_cart 中有 30 把香蕉和 10 个苹果
 93 banana_cart = [LineItem(‘banana‘, 30, .5),
 94                LineItem(‘apple‘, 10, 1.5)
 95 ]
 96
 97 #BulkItemPromo 为 joe 购买的香蕉优惠了 1.50 美元
 98 print(‘joe banana cart:‘, Order(joe, banana_cart, BulkItemPromo()))
 99
100 #long_order 中有 10 个不同的商品,每个商品的价格为 1.00 美元
101 long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]
102
103 #LargerOrderPromo 为 joe 的整个订单提供了 7% 折扣
104 print(‘joe 10个不同产品:‘, Order(joe, long_order, LargeOrderPromo()))
105 print(Order(joe, cart, LargeOrderPromo()))

以上代码执行的结果为:

joe 0积分: <Order total: 42.00 due: 42.00>
ann 1000积分: <Order total: 42.00 due: 39.90>
joe banana cart: <Order total: 30.00 due: 28.50>
joe 10个不同产品: <Order total: 10.00 due: 9.30>
<Order total: 42.00 due: 42.00>

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 19.5px Helvetica }

使用函数实现“策略”模式

??  Order 类和使用函数实现的折扣策略

 1 #顾客名称(name)和积分(fidelity)
 2 Customer = namedtuple(‘Customer‘, ‘name fidelity‘)
 3
 4
 5 #用于处理单个产品超过10个以上的类
 6 class LineItem:
 7
 8     def __init__(self, product, quantity, price):
 9         self.product = product
10         self.quantity = quantity
11         self.price = price
12
13     def total(self):
14         # 计算单个产品的总价
15         return self.price * self.quantity
16
17
18 class Order: # 上下文
19
20     # 用于接收顾客信息(Customer)购物车信息(cart)以及需要选用的折扣(promotion)
21     def __init__(self, customer, cart, promotion=None):
22         self.customer = customer
23         self.cart = list(cart)
24         self.promotion = promotion
25
26     #计算产品的价格
27     def total(self):
28         if not hasattr(self, ‘__total‘):
29             self.__total = sum(item.total() for item in self.cart)
30         return self.__total
31
32     #计算折扣后的价格
33     def due(self):
34         if self.promotion is None:
35             discount = 0
36         else:
37             #计算折扣只需调用 self.promotion()函数
38             discount = self.promotion(self)
39             return self.total() - discount
40
41     #用法前端repr()调用的是返回结果,如果不定义__str__则默认返回__repr__
42     def __repr__(self):
43         fmt = ‘<Order total: {:.2f} due: {:.2f}>‘
44         return fmt.format(self.total(), self.due())
45
46 def fidelity_promo(order):
47     """为积分为1000或以上的顾客提供5%折扣"""
48     return order.total() * .05 if order.customer.fidelity >= 1000 else 0
49
50 def bulk_item_promo(order):
51     """单个商品为20个或以上时提供10%折扣"""
52     discount = 0
53     for item in order.cart:
54         if item.quantity >= 20:
55             discount += item.total() * .1
56     return discount
57
58 def large_order_promo(order):
59     """订单中的不同商品达到10个或以上时提供7%折扣"""
60     distinct_items = {item.product for item in order.cart}
61     if len(distinct_items) >= 10:
62         return order.total() * .07
63     return 0
64
65 joe = Customer(‘John Doe‘, 0)
66 ann = Customer(‘Ann Smith‘, 1100)
67 cart = [LineItem(‘banana‘, 4, .5),
68         LineItem(‘apple‘, 10, 1.5),
69         LineItem(‘watermellon‘, 5, 5.0)]
70
71 print(Order(joe, cart, fidelity_promo))
72 print(Order(ann, cart, fidelity_promo))
73
74 banana_cart = [LineItem(‘banana‘, 30, .5),
75                LineItem(‘apple‘, 10, 1.5)]
76
77 print(Order(joe, banana_cart, bulk_item_promo))
78
79 long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]
80 print(Order(joe, long_order, large_order_promo))
81 print(Order(joe, cart, large_order_promo))

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 19.5px Helvetica }

选择最佳策略:简单的方式

我们继续使用上面实例的顾客和购物车,在此基础上添加 3 个测试:

 1 from collections import namedtuple
 2
 3
 4 #顾客名称(name)和积分(fidelity)
 5 Customer = namedtuple(‘Customer‘, ‘name fidelity‘)
 6
 7
 8 #用于处理单个产品超过10个以上的类
 9 class LineItem:
10
11     def __init__(self, product, quantity, price):
12         self.product = product
13         self.quantity = quantity
14         self.price = price
15
16     def total(self):
17         # 计算单个产品的总价
18         return self.price * self.quantity
19
20
21 class Order: # 上下文
22
23     # 用于接收顾客信息(Customer)购物车信息(cart)以及需要选用的折扣(promotion)
24     def __init__(self, customer, cart, promotion=None):
25         self.customer = customer
26         self.cart = list(cart)
27         self.promotion = promotion
28
29     #计算产品的价格
30     def total(self):
31         if not hasattr(self, ‘__total‘):
32             self.__total = sum(item.total() for item in self.cart)
33         return self.__total
34
35     #计算折扣后的价格
36     def due(self):
37         if self.promotion is None:
38             discount = 0
39         else:
40             #计算折扣只需调用 self.promotion()函数
41             discount = self.promotion(self)
42             return self.total() - discount
43
44     #用法前端repr()调用的是返回结果,如果不定义__str__则默认返回__repr__
45     def __repr__(self):
46         fmt = ‘<Order total: {:.2f} due: {:.2f}>‘
47         return fmt.format(self.total(), self.due())
48
49 def fidelity_promo(order):
50     """为积分为1000或以上的顾客提供5%折扣"""
51     return order.total() * .05 if order.customer.fidelity >= 1000 else 0
52
53 def bulk_item_promo(order):
54     """单个商品为20个或以上时提供10%折扣"""
55     discount = 0
56     for item in order.cart:
57         if item.quantity >= 20:
58             discount += item.total() * .1
59     return discount
60
61 def large_order_promo(order):
62     """订单中的不同商品达到10个或以上时提供7%折扣"""
63     distinct_items = {item.product for item in order.cart}
64     if len(distinct_items) >= 10:
65         return order.total() * .07
66     return 0
67
68
69 promos = [fidelity_promo, bulk_item_promo, large_order_promo]
70
71 #选用最佳折扣
72 def best_promo(order):
73     return max(promo(order) for promo in promos)
74
75 joe = Customer(‘John Doe‘, 0)
76 ann = Customer(‘Ann Smith‘, 1100)
77 cart = [LineItem(‘banana‘, 4, .5),
78         LineItem(‘apple‘, 10, 1.5),
79         LineItem(‘watermellon‘, 5, 5.0)]
80
81 banana_cart = [LineItem(‘banana‘, 30, .5), LineItem(‘apple‘, 10, 1.5)]
82
83 long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]
84
85 #best_promo 为顾客 joe 选择 larger_order_promo
86 print(Order(joe, long_order, best_promo))
87 #订购大量香蕉时,joe 使用 bulk_item_promo 提供的折扣
88 print(Order(joe, banana_cart, best_promo))
89 #在一个简单的购物车中,best_promo 为忠实顾客 ann 提供fidelity_promo 优惠的折扣
90 print(Order(ann, cart, best_promo))

以上代码执行的结果为:

<Order total: 10.00 due: 9.30>
<Order total: 30.00 due: 28.50>
<Order total: 42.00 due: 39.90>

 

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 19.5px Helvetica }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica }

时间: 2024-11-02 21:12:53

Python 使用一等函数实现设计模式的相关文章

流畅的python 使用一等函数实现设计模式

案例分析:重构“策略”模式 经典的“策略”模式 电商领域有个功能明显可以使用“策略”模式,即根据客户的属性或订单中的商品计算折扣.假如一个网店制定了下述折扣规则. 有 1000 或以上积分的顾客,每个订单享 5% 折扣. 同一订单中,单个商品的数量达到 20 个或以上,享 10% 折扣. 订单中的不同商品达到 10 个或以上,享 7% 折扣. 简单起见,我们假定一个订单一次只能享用一个折扣. 上下文 把一些计算委托给实现不同算法的可互换组件,它提供服务.在这个电商示例中,上下文是 Order,它

python技巧 一等函数

函数在python中作为一等函数,具有以下特点: 1.可以作为参数传递给其他函数 2.作为其他函数的值返回 3.能赋值给变量或数据结构中的元素 4.在运行的时候创建 In [1]: def add(x,y):   ...:     return x+y   ...: In [2]: new_add=add In [3]: new_addOut[3]: <function __main__.add> In [4]: new_add(3,4)Out[4]: 7 In [6]: list(map(n

Python编程实战:运用设计模式、并发和程序库创建高质量程序 阅读笔记

Python编程实战:运用设计模式.并发和程序库创建高质量程序 目录 1 创建型设计模式 2 结构型设计模式 3 行为型设计模式 4 高级并发 5 扩充Python 6 高级网络编程 7 Tkinter 8 OpenGL 创建型设计模式 抽象工厂 @classmethod def make_xxx(Class, ...) Builder with open(filename, "w", encoding='utf-8') as f: f.write(x) 多一层映射封装好吗? 序列与m

如何理解Python的Main函数?

作者:蟒蛇帝国(ID:Pythondg) 难度:初级 演示环境:OS:ubuntu 16.04Python:3.6 编写 Python 代码的时候我们经常看到下面这条语句.貌似是 Python 的 Main 函数.那它具体是什么意思呢. if __name__ == '__main__':     print('hello world') 首先 Python 里有两个概念, 源码文件: ~/code_house/py_dev$ tree . ├── file1.py ├── file2.py └

python 集合、函数

*集合: set:持有一系列元素,但是set元素没有重复,并且无序 如何创建:set()并传入一个list,list的元素将作为set 的元素. s=set(['a','b','c']) print(s)    //set(['a', 'c', 'b']) print(len(s))  //3 如何访问:用in操作符判断有没有 =([,,,]) ( )//True set的特点: 1.内部结构和字典很像,唯一区别是不存储value =([,,,,,,]) x1=x2=x1 :     () (x

python基础教程函数参数

python里有很多的内置函数给我们的工作带来了很多发便利,在我们实现某些功能或者优化代码的时候,可以自己定义一个函数,同时我们在定义一个类的时候也会用到函数的一些知识去构造一个方法,这里就涉及到一些关于函数的基础知识和函数传参的知识. 一.函数的相关介绍 1.函数定义:函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可. 函数特性: 减少重复代码 使程序变的可扩展 使程序变得易维护 2.函数的定义示例 定义一个函数要使用def语句,依次写出函数名.括

Python的功能函数如何使用?

Python开发中的函数提供了有组织的,可重用的代码来执行一组操作.函数简化了编码过程,防止冗余逻辑,并使代码更容易遵循. 定义和调用简单函数 使用 def 语句来定义Python中的函数最常见的方式. 这种说法是所谓的单条复合语句 的语法如下: def function_name(parameters): statement(s) function_name 被称为 标识符 的功能. 由于函数定义是一个可执行语句其执行 绑定 功能名称可以在以后使用的标识符被称为函数对象. parameters

python基础——sorted()函数

python基础——sorted()函数 排序算法 排序也是在程序中经常用到的算法.无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小.如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来. Python内置的sorted()函数就可以对list进行排序: >>> sorted([36, 5, -12, 9, -21]) [-21, -12, 5, 9, 36] 此外,sorted()函数也是一个

python内置函数proprety()

property 可以将python定义的函数"当做"属性访问,从而提供更加友好访问方式,但是有时候setter/getter也是需要的 假设定义了一个类Cls,该类必须继承自object类,有一私有变量__x 1. 第一种使用属性的方法: class Cls(object):     def __init__(self):         self.__x = None           def getx(self):         return self.__x