python[进阶] 6.使用一等函数实现设计模式

文章目录

    • 6.1.1 经典的“策略”模式
    • 6.1.2 使用函数实现“策略”模式
    • 6.1.3 选择最佳策略:简单的
    • 6.1.4 找出模块中的全部
  • 6.2 “命令”模式

6.1.1 经典的“策略”模式

为抽象基类(Abstract Base Class,ABC),这么做是为了使用 @abstractmethod 装饰器
来个例子:

from abc import ABC, abstractmethod
from collections import namedtuple
Customer = namedtuple('Customer', 'name fidelity')
class LineItem:def __init__(self, product, quantity, price):self.product = productself.quantity = quantityself.price = pricedef total(self):return self.price * self.quantityclass Order: #上下文def __init__(self, customer, cart, promotion=None):self.customer = customerself.cart = list(cart)self.promotion = promotiondef total(self):if not hasattr(self, '__total'):self.__total = sum(item.total() for item in self.cart)return self.__totaldef due(self):if self.promotion is None:discount = 0else:discount = self.promotion.discount(self)return self.total() - discountdef __repr__(self):fmt = '<Order total: {:.2f} due: {:.2f}>'return fmt.format(self.total(), self.due())
class Promotion (ABC): #抽象基类@abstractmethoddef discount(self, order):""""返回折扣金额"""class FidelityPromo(Promotion):#第一个具体策略"""为积分为1000或者以上的的顾客提供5%折扣"""def discount(self, order):return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0class BulkItemPromo(Promotion):#第二个具体策略"""单个商品为20个或以上时提供10%折扣"""def discount(self, order):discount = 0for item in order.cart:if item.quantity >= 20:discount += item.total() *0.1return discountclass 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() * 0.07return 0if __name__ == '__main__':joe = Customer('John Doe', 0)ann = Customer('Ann Smith', 1000)cart = [LineItem('banana', 4, 0.5),LineItem('apple', 10, 1.5),LineItem('watermellon', 5, 5.0)]print(Order(joe, cart, FidelityPromo()))   # <Order total: 42.00 due: 42.00>print(Order(ann, cart, FidelityPromo()))   #<Order total: 42.00 due: 39.90>banana_cart = [LineItem('banana', 30, 0.5),LineItem('apple', 10, 1.5)]print(Order(joe, banana_cart, BulkItemPromo())) #<Order total: 30.00 due: 28.50>long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]print(Order(joe, long_order, LargeOrderPromo()))  #<Order total: 10.00 due: 9.30>print(Order(joe, cart, LargeOrderPromo())) #<Order total: 42.00 due: 42.00>

上下文 - 把一些计算委托给实现不同算法的可互换组件,它提供服务。在这个电商示例中,上下文是 Order,它会根据不同的算法计算促销折扣。
策略 - 实现不同算法的组件共同的接口。在这个示例中,名为 Promotion 的抽象类扮演这
个角色。
具体策略 - “策略”的具体子类。fidelityPromo、BulkPromo 和 LargeOrderPromo 是这里实现
的三个具体策略。

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

这个例子是对上面的例子 的重构,把具体策略换成了简单的函数,而且去掉了Promotion 抽象类

from abc import ABC, abstractmethod
from collections import namedtuple
Customer = namedtuple('Customer', 'name fidelity')
class LineItem:def __init__(self, product, quantity, price):self.product = productself.quantity = quantityself.price = pricedef total(self):return self.price * self.quantityclass Order: #上下文def __init__(self, customer, cart, promotion=None):self.customer = customerself.cart = list(cart)self.promotion = promotiondef total(self):if not hasattr(self, '__total'):self.__total = sum(item.total() for item in self.cart)return self.__totaldef due(self):if self.promotion is None:discount = 0else:discount = self.promotion(self)return self.total() - discountdef __repr__(self):fmt = '<Order total: {:.2f} due: {:.2f}>'return fmt.format(self.total(), self.due())def fidelity_promo(order):"""为积分为1000或者以上的的顾客提供5%折扣"""return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0def bulk_item_promo(order):"""单个商品为20个或以上时提供10%折扣"""discount = 0for item in order.cart:if item.quantity >= 20:discount += item.total() *0.1return discountdef large_order_promo(order):"""订单中的不同商品达到10个或以上时提供7%折扣"""distinct_items = {item.product for item in order.cart}if len(distinct_items) >= 10:return order.total() * 0.07return 0if __name__ == '__main__':joe = Customer('John Doe', 0)ann = Customer('Ann Smith', 1000)cart = [LineItem('banana', 4, 0.5),LineItem('apple', 10, 1.5),LineItem('watermellon', 5, 5.0)]print(Order(joe, cart, fidelity_promo))   # <Order total: 42.00 due: 42.00>print(Order(ann, cart, fidelity_promo))   #<Order total: 42.00 due: 39.90>banana_cart = [LineItem('banana', 30, 0.5),LineItem('apple', 10, 1.5)]print(Order(joe, banana_cart, bulk_item_promo)) #<Order total: 30.00 due: 28.50>long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]print(Order(joe, long_order, large_order_promo))  #<Order total: 10.00 due: 9.30>print(Order(joe, cart, large_order_promo)) #<Order total: 42.00 due: 42.00>

❶ 计算折扣只需调用 self.promotion() 函数。
❷ 没有抽象类。
❸ 各个策略都是函数。
注意示例子中的标注:没必要在新建订单时实例化新的促销对象,函数拿来即用。

  • best_promo 函数计算所有折扣,并返回额度最大的

6.1.3 选择最佳策略:简单的

from abc import ABC, abstractmethod
from collections import namedtuple
Customer = namedtuple('Customer', 'name fidelity')
class LineItem:def __init__(self, product, quantity, price):self.product = productself.quantity = quantityself.price = pricedef total(self):return self.price * self.quantityclass Order: #上下文def __init__(self, customer, cart, promotion=None):self.customer = customerself.cart = list(cart)self.promotion = promotiondef total(self):if not hasattr(self, '__total'):self.__total = sum(item.total() for item in self.cart)return self.__totaldef due(self):if self.promotion is None:discount = 0else:discount = self.promotion(self)return self.total() - discountdef __repr__(self):fmt = '<Order total: {:.2f} due: {:.2f}>'return fmt.format(self.total(), self.due())def fidelity_promo(order):"""为积分为1000或者以上的的顾客提供5%折扣"""return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0def bulk_item_promo(order):"""单个商品为20个或以上时提供10%折扣"""discount = 0for item in order.cart:if item.quantity >= 20:discount += item.total() *0.1return discountdef large_order_promo(order):"""订单中的不同商品达到10个或以上时提供7%折扣"""distinct_items = {item.product for item in order.cart}if len(distinct_items) >= 10:return order.total() * 0.07return 0promos = [fidelity_promo,bulk_item_promo, large_order_promo]
def best_promo(order):"""选择可用的最佳折扣"""return max(promo(order) for promo in promos)if __name__ == '__main__':joe = Customer('John Doe', 0)ann = Customer('Ann Smith', 1000)cart = [LineItem('banana', 4, 0.5),LineItem('apple', 10, 1.5),LineItem('watermellon', 5, 5.0)]# print(Order(joe, cart, fidelity_promo))   # <Order total: 42.00 due: 42.00># print(Order(ann, cart, fidelity_promo))   #<Order total: 42.00 due: 39.90>banana_cart = [LineItem('banana', 30, 0.5),LineItem('apple', 10, 1.5)]# print(Order(joe, banana_cart, bulk_item_promo)) #<Order total: 30.00 due: 28.50>long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]# print(Order(joe, long_order, large_order_promo))  #<Order total: 10.00 due: 9.30># print(Order(joe, cart, large_order_promo)) #<Order total: 42.00 due: 42.00>print(Order(joe, long_order, best_promo))print(Order(joe, banana_cart, best_promo))print(Order(ann, cart, best_promo))

6.1.4 找出模块中的全部

在 Python 中,模块也是一等对象,而且标准库提供了几个处理模块的函数。Python 文档
是这样说明内置函数 globals 的。

globals() 函数会以字典类型返回当前位置的全部全局变量
例子:

a= "hello"
print(globals())
#{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f0272b221d0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/maxzhang/PycharmProjects/pythoncode/test.py', '__cached__': None, 'a': 'hello'}

下面的实例 使用 globals 函数帮助 best_promo 自动找到其他可用的 *_promo 函数,过程
有点曲折。

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)

收集所有可用促销的另一种方法是,在一个单独的模块中保存所有策略函数,把
best_promo 排除在外。
在示例 6-8 中,最大的变化是内省名为 promotions 的独立模块,构建策略函数列表。注意,示例 6-8 要导入 promotions 模块,以及提供高阶内省函数的 inspect 模块(简单起见,这里没有给出导入语句,因为导入语句一般放在文件顶部)。
示例 6-8 内省单独的 promotions 模块,构建 promos 列表,inspect模块用于收集python对象的信息,可以获取类或函数的参数的信息,源码,解析堆栈,对对象进行类型检查等等。

import inspect
promos = [func for name, func ininspect.getmembers(promotions, inspect.isfunction)]
def best_promo(order):"""选择可用的最佳折扣"""return max(promo(order) for promo in promos)

inspect.getmembers 函数用于获取对象(这里是 promotions 模块)的属性,第二个
参数是可选的判断条件(一个布尔值函数)。我们使用的是 inspect.isfunction,只
获取模块中的函数。
动态收集促销折扣函数更为显式的一种方案是使用简单的装饰器。第 7 章讨论函数装饰器
时会使用其他方式实现这个电商“策略”模式示例。

6.2 “命令”模式

“命令”设计模式也可以通过把函数作为参数传递而简化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/273809.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2014阿里巴巴校园招聘笔试题 - 中南站

转载于:https://www.cnblogs.com/gotodsp/articles/3530329.html

python中一些特殊方法的作用

我们先暂且称呼为特殊方法。 单下划线开头&#xff08;_foo&#xff09;双下划线开头的&#xff08;__foo&#xff09;双下划线开头和结尾的&#xff08; __foo__&#xff09;代表不能直接访问的类属性&#xff0c;需通过类提供的接口进行访问&#xff0c;不能用“from xxx im…

Spring的IOC原理[通俗解释一下]

1. IoC理论的背景 我们都知道&#xff0c;在采用面向对象方法设计的软件系统中&#xff0c;它的底层实现都是由N个对象组成的&#xff0c;所有的对象通过彼此的合作&#xff0c;最终实现系统的业务逻辑。 图1&#xff1a;软件系统中耦合的对象 如果我们打开机械式手表的后盖&am…

python爬虫面试遇到的问题

文章目录&#xff11;python基础1.1 列表生成式和生成器的区别 &#xff1f;1.2 如何不用任何循环快速筛掉列表中的奇数元素 &#xff1f;1.3 map和reduce的用法1.4 装饰器的作用1.5 Python中__new__与__init方法的区别1.6 python中的设计模式1.7 lambda函数&#xff0c;以及它…

ubuntu18 常用命令

文章目录卸载和安装卸载和安装 1.打开一个终端&#xff0c;输入dpkg --list ,按下Enter键&#xff0c;终端输出以下内容&#xff0c;显示的是你电脑上安装的所有软件。 2.安装 dpkg –i name.deb 安装一个 deb 包&#xff1b;在终端上输入命令sudo apt-get --purge remove 包…

以嵌入式系统设计师考试成绩,开始嵌入式博客之旅

http://www.rkb.gov.cn/jsj/cms/s_contents/download/s_dt201003110106.html 转载于:https://www.cnblogs.com/yueqian-scut/p/3952268.html

SSH框架配置及Maven使用

1.SSH框架配置 1.1. SSH框架介绍 1.2. SSH框架配置 所需资源下载&#xff1a; l jdk; 从Oracle官方网站&#xff1a;http://www.oracle.com/technetwork/cn/java/javase/downloads/index.html下载jdk&#xff0c;win7是默认安装在C:\Program Files (x86)\Java\jdk1.6.0_25路径下…

cookie,session的区别和联系(补充token)

文章目录1 http为什么是无状态的2 cookie 和session 的区别详解3 token参考&#xff1a;备注: 博客文章仅限于学习&#xff0c;禁止商用1 http为什么是无状态的 2 cookie 和session 的区别详解 这些都是基础知识&#xff0c;不过有必要做深入了解。先简单介绍一下。 二者的定…

库函数和系统调用的区别

前言 这是一对非常容易混淆的概念。对于用户( 应用程序开发者 )来说&#xff0c;并不一定要严格区分其意义。因为在用户看来&#xff0c;它们都是以C函数的形式出现的。但了解二者的区别对我们掌握整个计算机系统有很大帮助。 区别 1. 一部分库函数实现需要使用系统调用( 如 pr…

Flask 从入门到熟悉(不敢称为精通)

文章目录2.1 Flask介绍及其安装2.2 Virtualenv3.1 一个最小的应用3.2 外部课件服务器3.3 调试模式4.1 路由介绍4.2 变量规则4.3 构建URL4.4 HTTP 方法4 总结5.1 静态文件5.2 渲染模板5.3 练习66.1 接收请求数据6.2 请求对象6.3 文件上传6.4 Cookies6 总结77.1 重定向和错误7.2 …

Ext JS 5 beta版发布

原文&#xff1a;Announcing Public Beta of Ext JS 5我们非常高兴的宣布&#xff0c;Sencha Ext JS 5 beta版本开始进行公测了。这个beta版本可以让你、我们Sencha社区来对我们的Ext JS 5的工作进度进行评测。对于所以Ext JS开发人员&#xff0c;这事一个很好的机会来协助完成…

算法【二分查找】(数组)

1 .山脉数组的巅峰索引 信息 我们把符合下列属性的数组 A 称作山脉&#xff1a; A.length > 3 存在 0 < i < A.length - 1 使得A[0] < A[1] < … A[i-1] < A[i] > A[i1] > … > A[A.length - 1] 给定一个确定为山脉的数组&#xff0c;返回任何满…

关于癌症的十大谣言

最近&#xff0c;国外网站总结了西方社会中流行的十个关于癌症的谣言&#xff0c;其中很多谣言在我们周围也有广泛的传播。 谣言1&#xff1a;癌症是人为导致的现代疾病 或许在公众的认知里&#xff0c;癌症在今天要比历史上任何时期都重要。不过实际上&#xff0c;癌症可不是一…

[python 进阶] 第7章 函数装饰器和闭包

文章目录7.1 装饰器基础知识7.2 Python何时执行装饰器7.3 使用装饰器改进“策略”7.4 变量作用域(global)备注 -比较字节码&#xff08;暂略&#xff09;7.5 闭包7.6 nonlocal声明global和nonlocal的区别7.7 实现一个简单的装饰器7.8 标准库中的装饰器7.8.1 使用functools.lru_…

自制“低奢内”CSS3登入表单,包含JS验证,请别嫌弃哦。

要求 必备知识 基本了解CSS语法,初步了解CSS3语法知识。和JS/JQuery基本语法。 开发环境 Adobe Dreamweaver CS6 演示地址 演示地址 预览截图(抬抬你的鼠标就可以看到演示地址哦): 制作步骤: 一, html结构 <div id"home"><form id"login" class…

class里面只能写以下5种

转载于:https://www.cnblogs.com/phplearnings/p/3650849.html

【排序】算法(python实现)

文章目录python 排序算法1 插入排序1.1 直接插入排序算法思想1.2 希尔排序算法思想2. 选择排序2.1 简单选择排序2.2 堆排序参考python 排序算法 1 插入排序 1.1 直接插入排序 算法思想 直接插入排序的核心思想就是&#xff1a;将数组中的所有元素依次跟前面已经排好的元素相…

OpenSSL漏洞补救办法详解(转)

CVE-2014-0160漏洞背景 2014年4月7日OpenSSL发布了安全公告&#xff0c;在OpenSSL1.0.1版本中存在严重漏洞(CVE-2014-0160)。OpenSSL Heartbleed模块存在一个BUG&#xff0c;问题存在于ssl/dl_both.c文件中的心跳部分&#xff0c;当攻击者构造一个特殊的数据包&#xff0c;满足…

SharePoint 自定义WebPart之间的连接

1、创建SharePoint解决方案&#xff0c;添加两个WebPart分别用来发送和接收&#xff1b; 2、发送值的WebPart需要继承自IWebPartField(当然&#xff0c;根据需要还可以选择IWebPartField,IWebPartParameters,IWebPartRow,IWebPartTable&#xff0c;具体参见msdn)&#xff0c;原…