当前位置: 首页 > news >正文

Python函数式编程进阶:用函数实现设计模式

文章目录

  • 函数式编程进阶:用函数实现设计模式
    • 案例实现:构建“策略”模式
      • 使用函数实现”策略“模式
        • 享元
      • 选择最佳策略:简单的方式
    • globals关键字

函数式编程进阶:用函数实现设计模式

案例实现:构建“策略”模式

策略模式:我们把一系列算法封装起来,使得他们可以相互替换,本模式可以独立于他们的客户而变化

from abc import ABC, abstractmethod
from collections import namedtupleCustomer = namedtuple("Customer",'name fidelity')class LineItem:def __init__(self,product,quantity,price) -> None:self.product = productself.quantity = quantityself.price = pricedef total(self):return self.quantity * self.priceclass Order: # 上下文def __init__(self, customer, cart, promotion=None) -> 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) -> str:fmt = '<Order total: {:.2f} due: {:.2f}'return fmt.format(self.total,self.due)class Promotion(ABC): #抽象基类@abstractmethoddef discount(self, order):"""返回折扣金额"""class FidelityPromo(Promotion):def discount(self, order):"""积分为1000以上的顾客提供5%的折扣"""return order.total() * .05 if order.customer.fidelity >= 1000 else 0class BulkItemPromo(Promotion):def discount(self,order):"""单个商品为20个或以上时提供10%折扣"""discount = 0 for item in order.cart:if item.quantity >= 20:discount += item.total() * .10return discountclass LargeOrderPromo(Promotion):"""订单中的不同商品达到10个以上时提供7%折扣"""def discount(self,order):discount = 0for item in order.cart:if item.quantity >= 10:discount += item.total() * .07return discount

这个实例中我们实例化了所有的策略,还有客户订单,使用抽象基类和抽象方法装饰符来明确所用的方式。

测试以上用例

joe = Customer('John Doe', 0)
ann = Customer('Ann Smith',1000)
cart = [LineItem('banana',4,.5),LineItem('apple',10,1.5),LineItem('watermelon',5,5.0)]order_joe = Order(joe,cart,FidelityPromo())
order_ann = Order(ann,cart,FidelityPromo())
print(repr(order_ann))
print(repr(order_joe))
# 输出
# <Order total: 42.00 due: 39.90>
# <Order total: 42.00 due: 42.00>

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

以上实例都是基于类实现的,而且每个类都只定义了一个方法,而且每个实例都没有自己的状态,看起来和普通的函数没有区别

我们可以把具体策略换成函数实现

from abc import ABC, abstractmethod
from collections import namedtupleCustomer = namedtuple("Customer",'name fidelity')class LineItem:def __init__(self,product,quantity,price) -> None:self.product = productself.quantity = quantityself.price = pricedef total(self):return self.quantity * self.priceclass Order: # 上下文def __init__(self, customer, cart, promotion=None) -> 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) -> str: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 0def bulk_item_promo(order):"""单个商品20个以上提供10%的折扣"""discount = 0 for item in order.cart:if item.quantity >= 20:discount += item.total() * .1return discountdef large_order_promo(order):"""订单中不同商品的个数达到10个以上时提供7%的折扣"""distinct_item = {item.product for item in order.cart}if len(distinct_item >= 10):return order.total() * .07return 0joe = Customer('John Doe', 0)
ann = Customer('Ann Smith',1000)
cart = [LineItem('banana',4,.5),LineItem('apple',10,1.5),LineItem('watermelon',5,5.0)]order_joe = Order(joe,cart,fidelity_promo)
order_ann = Order(ann,cart,fidelity_promo)
print(repr(order_ann))
print(repr(order_joe))

把折扣策略通过函数实现可以减少一部分的代码量,但是以上两种办法,都没有办法实现最佳调用方法,它们缺少内部状态

这些具体策略都没有内部状态,只是单纯的对上下文进行处理

这个时候需要引入享元的做法

享元

享元是可以共享的对象,同时可以在多个上下文中使用,这样不必再新的上下文中根据不同策略不断创建新的实例对象

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

promos = [fidelity_promo,bulk_item_promo,large_order_promo]
def best_promo(order):return max(promo(order) for promo in promos)

以上代码可用但是如果添加新的方法就需要把他加到promos列表中否则best_promo函数不会考虑新的策略,要如何保证新加的策略立刻就能应用到bestpromo函数呢

globals关键字

globals()是python的一个内置方法,表示当前的全局符号表.

比如当我在程序运行最后打印这个关键字,其返回值是一个字典

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000234CBDD6CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'c:\\Users\\Administrator\\GithubRepo\\study_recording\\fluent_python\\ch06_07\\functional_pattern_design.py', '__cached__': None, 'ABC': <class 'abc.ABC'>, 'abstractmethod': <function abstractmethod at 0x00000234CC2780D0>, 'namedtuple': <function namedtuple at 0x00000234CC466550>, 'Customer': <class '__main__.Customer'>, 'LineItem': <class '__main__.LineItem'>, 'Order': <class '__main__.Order'>, 'fidelity_promo': 
<function fidelity_promo at 0x00000234CC486DC0>, 'bulk_item_promo': <function bulk_item_promo at 0x00000234CC4881F0>, 'large_order_promo': <function large_order_promo at 0x00000234CC488280>, 'promos': [<function fidelity_promo at 0x00000234CC486DC0>, <function bulk_item_promo at 0x00000234CC4881F0>, <function large_order_promo at 0x00000234CC488280>], 'best_promo': <function best_promo at 0x00000234CC488310>, 'joe': Customer(name='John Doe', fidelity=0), 'ann': Customer(name='Ann Smith', fidelity=1000), 'cart': [<__main__.LineItem object at 0x00000234CC281430>, <__main__.LineItem object at 0x00000234CC2D66A0>, <__main__.LineItem object at 0x00000234CC2D68E0>], 'banana_cart': [<__main__.LineItem object at 0x00000234CC2D62E0>, <__main__.LineItem object at 0x00000234CC2D63A0>], 'order_joe': <Order total: 42.00 due: 42.00>, 'order_ann': <Order total: 42.00 due: 39.90>, 'banana_order_joe': <Order total: 30.00 due: 28.50>, 'banana_order_ann': <Order total: 30.00 due: 28.50>}

可以利用这个全局符号表来帮我们找到一些刚创建的策略

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)

相关文章:

Python函数式编程进阶:用函数实现设计模式

文章目录 函数式编程进阶&#xff1a;用函数实现设计模式案例实现&#xff1a;构建“策略”模式使用函数实现”策略“模式享元 选择最佳策略&#xff1a;简单的方式 globals关键字 函数式编程进阶&#xff1a;用函数实现设计模式 案例实现&#xff1a;构建“策略”模式 策略模…...

Ingress controller:Kubernetes 的瑞士军刀

原文作者&#xff1a;Brian Ehlert of F5 原文链接&#xff1a;Ingress controller&#xff1a;Kubernetes 的瑞士军刀 转载来源&#xff1a;NGINX 中文官网 NGINX 唯一中文官方社区 &#xff0c;尽在 nginx.org.cn 许多人认为 Ingress controller&#xff08;Ingress 控制器&…...

uniapp tabBar app页面滚动闪屏的问题

我在做app的时候&#xff0c;调试tabBar页面滚动时莫名其妙的闪屏&#xff0c;其他页面不闪屏&#xff0c;可能跟新建的项目样式有关。 修改方法如下。 在pages.json中 "tabBar": {"selectedColor": "#204AFF","color": "#ccc…...

【计算机毕业设计】388微信小程序足球赛事及队伍管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…...

监控易监测对象及指标之:华为FusionInsight Kafka服务全方位监控

监控易提供对华为FusionInsight Kafka服务的全方位监控功能。该功能可以帮助用户实时监测Kafka服务的各项性能指标&#xff0c;确保服务的稳定运行和高效性能。 具体功能点包括&#xff1a; 服务状态监测&#xff1a;监控易能够实时监测华为FusionInsight Kafka服务的运行状态…...

Python装饰器的应用

Python 中的装饰器是一种语法糖&#xff0c;可以在运行时&#xff0c;动态的给函数或类添加功能。装饰器本质上是一个函数&#xff0c;使用 函数名就是可实现绑定给函数的第二个功能 。它的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。 …...

【数据结构与算法 | 基础篇】力扣232, 225

1. 力扣232 : 用栈实现队列 (1). 题 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移…...

内网(极空间)搭建gitlab跳板机转发端口及域名配置

背景说明 https://blog.csdn.net/GodDavide/article/details/139182475 上文说到: 我已经用docker搭好了gitlab-ce服务&#xff0c;但我是部署在自己的家庭nas-极空间z4pro里的&#xff0c;属于内网环境。 另外我有一台阿里云服务器&#xff0c;做跳板机。 我有一个阿里的域名…...

如何知道自己电脑的 Shell类型是什么?

在macOS中&#xff0c;你可以通过以下几种方法来确定当前正在使用的shell类型&#xff0c;并了解相关的配置文件&#xff1a; 1. 使用终端命令确定shell类型 打开终端应用程序&#xff08;Terminal&#xff09;。输入以下命令并按回车键&#xff1a;echo $SHELL。该命令会输出…...

Axios的使用简单说明

axios 请求方式和参数 axios 可以发送 ajax 请求&#xff0c;不同的方法可以发送不同的请求: axios.get&#xff1a;发送get请求 axios.post&#xff1a;发送post请求 axios.put&#xff1a;发送put请求 axios.delete&#xff1a;发送delete请求 无论哪种方法&#xff0c;第一…...

查找list集合中,持续时间>=ContinueTime的数据集合,保存在新的list中

在给定的包含时间戳的list中&#xff0c;查找连续continueNum次的且时间间隔为needDiff的集合。 eg&#xff1a;相邻两个数据的时间戳间隔为1分钟&#xff0c;且超过30分钟有数据 /**** param list 包含时间戳&#xff08;10位&#xff09;的list* param continueNum 至少持续…...

nginx 反向代理配置详解

Nginx 反向代理是一种常用的部署策略&#xff0c;用于将客户端请求转发到内部网络中的一个或多个服务器&#xff0c;这些服务器直接处理请求并返回响应给Nginx&#xff0c;再由Nginx转交给客户端。这种设置可以提高网站的可用性和安全性&#xff0c;同时也能实现负载均衡、缓存…...

微信小程序毕业设计-农场驿站平台系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…...

CAN总线应用协议CANopen

作为一种真正开放的CAN总线高层协议&#xff0c;CANopen协议允许不同的CAN设备以标准化的方式进行通讯&#xff0c;这使得CAN 设备具有互操作性。随着CANopen协议的日益完善&#xff0c;它已经广泛应用于多个行业。本文将对CANopen协议的对象字典、通讯对象、网络管理等几个方面…...

htop安装不了怎么解决

&#x1f31f;&#x1f30c; 欢迎来到知识与创意的殿堂 — 远见阁小民的世界&#xff01;&#x1f680; &#x1f31f;&#x1f9ed; 在这里&#xff0c;我们一起探索技术的奥秘&#xff0c;一起在知识的海洋中遨游。 &#x1f31f;&#x1f9ed; 在这里&#xff0c;每个错误都…...

vue 笔记02

目录 01 事件修饰符 02 按键修饰符 03 v-bind属性 04 vue-axios的基本使用 05 vue的生命周期 06 vue生命周期涉及到的其他的知识点 01 事件修饰符 vue的事件修饰符 事件名称.修饰符1.修饰符2...事件驱动函数 stop 阻止冒泡修饰符 prevent 阻止默认行为 once 当前事件只触…...

MySQL8.0免安装及phpmyadmin配置

安装包解压&#xff0c;运行mysqld文件后&#xff0c;启动net start&#xff0c;提示成功&#xff0c;但进入phpmyadmin登录页面后&#xff0c;输入用户名&#xff0c;提示不支持空密码&#xff0c;config.default.php设置密码后&#xff0c;提示 mysqli::real_connect(): (HY…...

【目标解算】相机内外参数详细解读+坐标系转换

一、相机参数介绍 1.1 相机内参矩阵 概念&#xff1a;内参矩阵用于描述相机的内部参数&#xff0c;它包含了相机的焦距、主点坐标和图像的畸变等信息。内参矩阵的形式通常为一个3x3的矩阵&#xff0c;常用表示为K。内参矩阵可以将相机坐标系中的三维点映射到图像平面上的二维…...

【Unity】颜色混合计算

在图形渲染中&#xff0c;颜色混合&#xff08;Color Blending&#xff09;是指将多个颜色值组合在一起以生成最终显示的颜色。颜色混合技术广泛用于处理半透明效果、光照效果和后期处理效果。以下是一些常见的颜色混合模式&#xff1a; 1. 正常混合&#xff08;Normal Blendi…...

Vue源码解析

入门级 <body><div id"app"></div><script>class Vue {constructor(options) {// thisVue 把options.created的this 指向Vue实例options.created.bind(this)();// this.$el 指向#appthis.$el document.querySelector(options.el);// 把opt…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...