python的装饰器与设计模式中的装饰器模式

news/2024/4/26 18:59:22/文章来源:https://blog.csdn.net/qunsorber/article/details/129232373

相信很多人在初次接触python中的装饰器时,会跟我一样有个疑问,这跟设计模式中的装饰器模式有什么区别吗?本质上是一样的,都是对现有对象,包括函数或者类的一种扩展。这篇文档将进行对比分析。

python的装饰器

装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。

使用一个性能测试的例子来说明。当我们需要测试一段训练或者推理的时长时,可能会写出类似这样的代码,直接在一个训练函数里加入时间统计

def train(X, y):start = time.time()knn = KNeighborsTimeSeriesClassifier(n_neighbors=2)knn.fit(X, y)end  = time.time()train_time = end - startprint('train time cost : %.5f sec' %train_time)return knn

这是一段真实的代码片段,选自在某次训练任务过程中。当然如果不知道python的装饰器,在这个比较简短的函数体内,加入三行代码,其实也无伤大雅。但是既然山在那里,就要去攀登呀,因此强行上装饰器。

def getDuration(func):def wrapper(*args, **kwargs):print("%s is running" % func.__name__)start = time.time()result = func(*args, **kwargs)end = time.time()train_time = end - startprint('train time cost : %.5f sec' %train_time)return resultreturn wrapper@getDuration
def train(X, y):# start = time.time()knn = KNeighborsTimeSeriesClassifier(n_neighbors=2)knn.fit(X, y)# end  = time.time()# train_time = end - start# print('train time cost : %.5f sec' %train_time)return knn

在train上方加入@+函数名,即将train函数加上一层装饰器。train函数中原本记录时间的start和end,移到wrapper中。train的函数通过wrapper中的*args、**kwargs转发。

面向对象的装饰器模式

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。其结构图为

在这里插入图片描述

这个通用的结构看起来略显抽象,结合一个咖啡豆的例子来说明,其UML图为:

在这里插入图片描述

咖啡是由咖啡豆制作来的。而咖啡又根据糖分含量(或奶含量?)的比例不同分为美式、拿铁、摩卡、卡布奇洛等等。那不同的咖啡当然可以有咖啡这个类继承而来,但更灵活的方式是用装饰器模式。这里贴出这几个类的设计实现。

// 咖啡豆,抽象类,定义接口,可以显示咖啡类型和价格
class CoffeeBean
{
public:virtual void ShowCoffeeName() = 0;virtual void ShowPrice() = 0;public:std::string m_sCoffeeName;int m_iPrice;
};//对咖啡豆类的一份实现
class Coffee:public CoffeeBean
{
public:Coffee(std::string name,int price){m_sCoffeeName = name;m_iPrice = price;}~Coffee() {}void ShowCoffeeName(){std::cout << "CoffeeBean name:" << m_sCoffeeName << std::endl;}void ShowPrice(){std::cout << "CoffeeBean Price:" << m_iPrice << std::endl;}
};// 对咖啡豆抽象类的扩展,这个扩展类相当于在caffeebean类型上加上一个装饰器
// 的效果,不同的装饰器成为美式、拿铁和摩卡
class ExtendCoffee :public CoffeeBean
{
public:ExtendCoffee(CoffeeBean* pBean){m_pBean = pBean;}~ExtendCoffee(){}virtual void ShowCoffeeName() = 0;virtual void ShowPrice() = 0;protected:CoffeeBean* m_pBean;
};// 美式的实现版本(通过装饰器的方式)
class Americano :public ExtendCoffee
{
public:Americano(CoffeeBean* pBean):ExtendCoffee(pBean){}~Americano() {}void ShowCoffeeName(){std::cout << "I am Americano Coffee,Coffee name:" << m_pBean->m_sCoffeeName + " from American" << std::endl;}void ShowPrice(){m_pBean->m_iPrice = 48;std::cout << "Americano Coffee price:" << m_pBean->m_iPrice << std::endl;}
};// 拿铁的实现版本(通过装饰器的方式)
class Latte :public ExtendCoffee
{
public:Latte(CoffeeBean* pBean) :ExtendCoffee(pBean) {}~Latte() {}void ShowCoffeeName(){std::cout << "I am Latte Coffee,Coffee name:" << m_pBean->m_sCoffeeName + " from Italy" << std::endl;}void ShowPrice(){m_pBean->m_iPrice = 58;std::cout << "Latte Coffee price:" << m_pBean->m_iPrice << std::endl;}
};// 摩卡的实现版本(通过装饰器的方式)
class Mocha :public ExtendCoffee
{
public:Mocha(CoffeeBean* pBean) :ExtendCoffee(pBean) {}~Mocha() {}void ShowCoffeeName(){std::cout << "I am Mocha Coffee,Coffee name:" << m_pBean->m_sCoffeeName + " from Franch" << std::endl;}void ShowPrice(){m_pBean->m_iPrice = 68;std::cout << "Mocha Coffee price:" << m_pBean->m_iPrice << std::endl;}
};

这里的ExtendCoffee相当于抽象装饰,由此基础上实现了不同的装饰效果,即不同的咖啡类型。

优点

  • 不改动原有代码,动态增加功能。

  • 对象间不会相互依赖、松耦合。

  • 符合开闭原则,扩展性好,便于维护。

缺点

  • 装饰器环节过多的话,导致装饰器类膨胀。

  • 装饰器层层嵌套比较复杂,可能导致排查问题流程繁琐。

参考文档

Python 函数装饰器

理解 Python 装饰器看这一篇就够了

C++装饰器模式

设计模式装饰器模式

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

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

相关文章

duboo+zookeeper分布式架构入门

分布式 dubbo Zookeeper 分布式系统就是若干独立计算机的集合&#xff08;并且这些计算机之间相互有关联&#xff0c;就像是一台计算机中的C盘F盘等&#xff09;&#xff0c;这些计算对于用户来说就是一个独立的系统。 zookeeper安装 下载地址&#xff1a;Index of /dist/z…

【数据库系统概论】基础知识总结

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

C++10:非类型模板参数以及模板的特化

目录 非类型模板参数 模板的特化 模板类的特化 1.全特化 2.偏特化 模板其实还有其他的玩法&#xff0c;比如非类型模板参数以及模板的特化。 非类型模板参数 在记述非类型模板参数前&#xff0c;我们认识一下C中一个比较鸡肋的类&#xff0c;array #include<iostream&g…

k8s-yaml文件

文章目录一、K8S支持的文件格式1、yaml和json的主要区别2、YAML语言格式二、YAML1、查看 API 资源版本标签2、编写资源配置清单2.1 编写 nginx-test.yaml 资源配置清单2.2 创建资源对象2.3 查看创建的pod资源3、创建service服务对外提供访问并测试3.1 编写nginx-svc-test.yaml文…

数据仓库Hive

HIve介绍 Hive是建立在Hadoop上的数据仓库基础构架。它提供了一系列的工具&#xff0c;可以用来进行数据提取转化加载&#xff0c;可以简称为ETL。 Hive 定义了简单的类SQL查询语言&#xff0c;称为HQL&#xff0c;它允许熟悉SQL的用户直接查询Hadoop中的数据&#xf…

如何从0创建Spring Cloud Alibaba(多模块)

以一个父工程带两个Module&#xff08;test1、test2&#xff09;为例。 一、创建父工程 由于是模块化项目&#xff0c;那么父工程不需要实际的代码逻辑&#xff0c;因此无需创建src&#xff0c;那么可以有几种方式创建&#xff0c;例如&#xff1a; 使用Spring Initializr脚…

腾讯一面—Android 系统启动流程详解

正文AMS 是 Android 中最核心的服务之一&#xff0c;主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作&#xff0c;其职责与操作系统中的进程管理和调度模块相类似&#xff0c;它本身也是一个 Binder 的实现类&#xff0c;应用进程能通过 Binder 机制调用…

ARM Context synchronization event和Instruction Synchronization Barrier

在Arm architecture里&#xff0c;经常提到Context synchronization event(CSE)和Explicit synchronization&#xff0c;Context synchronization events在之前是叫作context synchronization operations。Explicit synchronization是Context synchronization event的结果&…

基于yolov5与改进VGGNet的车辆多标签实时识别算法

摘 要 为了能快速、有效地识别视频中的车辆信息&#xff0c;文中结合YOLOv3算法和CNN算法的优点&#xff0c;设计了一种能实时识别车辆多标签信息的算法。首先&#xff0c;利用具有较高识别速度和准确率的YOLOv3实现对视频流中车辆的实时监测和定位。在获得车辆的位置信息后…

如何提高机器人专业课讲师的收入

先放一些总结&#xff1a;为什么我是不合格的高校机器人工程专业讲师&#xff1f;2020不合格肯定收入不会提升&#xff0c;甚至失业风险会非常高的。为何所做的课程努力几乎全部失败呢&#xff1f;→机器人工程类← 2022不能一次次失败&#xff0c;因为只有自己会为失败买单&am…

CUDA 内存系统

CUDA 内存系统 本文主要是针对<cuda c编程权威指南>的总结,由于原书出版的时候cuda刚刚出到cuda6,之后的cuda版本可能有更新,可能需要我翻一翻文档,待更新. 内存系统架构图 常见的内存作用域与生存期 新特性 早期的 Kepler 架构中一个颇为好用的特性就是 CUDA 程序员可…

JVM - G1垃圾收集器深入剖析

​​​​​​​1、G1收集器概述 HotSpot团队一直努力朝着高效收集、减少停顿(STW: Stop The World)的方向努力&#xff0c;也贡献了从串行Serial收集器、到并行收集器Parallerl收集器&#xff0c;再到CMS并发收集器&#xff0c;乃至如今的G1在内的一系列优秀的垃圾收集器。 G…

Spring Cache的基本使用与分析

概述 使用 Spring Cache 可以极大的简化我们对数据的缓存&#xff0c;并且它封装了多种缓存&#xff0c;本文基于 redis 来说明。 基本使用 1、所需依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-…

随想录二刷Day06——链表

文章目录链表6. 删除链表的倒数第 N 个结点7. 链表相交8. 环形链表 II链表 6. 删除链表的倒数第 N 个结点 19. 删除链表的倒数第 N 个结点 思路&#xff1a; 用双指针的方法&#xff0c;fast 和 slow 之间保持距离为 n&#xff0c;只需要遍历一次即可完成删除任务。 为了方便…

使用jenkins实现自动化部署springboot应用

1. 前置准备 这里代码仓库使用gitlab。在介绍如何通过gitlab和jenkins进行自动化部署之前&#xff0c;需要先安装完成gitlab以及jenkins。两种程序的安装方式以及相关配置可以参看以下内容&#xff1a; linux中安装gitlab&#xff1a;linux安装极狐gitlab linux中安装jenki…

EasyRecovery16最新免费版电脑数据恢复软件功能介绍

EasyRecovery是一款支持Windows/Mac平台进行恢复图片的专业工具&#xff0c;尤其是各种流行单反相机RAW格式文件&#xff0c;以及超大型视频文件等&#xff0c;推荐摄影爱好者使用。适用于主流相机、无人机、PC、存储卡、USB 闪存驱动器等&#xff0c;由于删除、损坏或意外格式…

[数据结构]:05-循环队列(链表)(C语言实现)

目录 前言 已完成内容 循环队列实现 01-开发环境 02-文件布局 03-代码 01-主函数 02-头文件 03-QueueCommon.cpp 04-QueueFunction.cpp 结语 前言 此专栏包含408考研数据结构全部内容&#xff0c;除其中使用到C引用外&#xff0c;全为C语言代码。使用C引用主要是为了…

CountDownLatch与CyclicBarrier原理剖析

1.CountDownLatch 1.1 什么是CountDownLatch CountDownLatch是一个同步工具类&#xff0c;用来协调多个线程之间的同步&#xff0c;或者说起到线程之间的通信&#xff08;而不是用作互斥的作用&#xff09;。 CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之…

学习网安需要了解的一些基础知识

P1.基本概念 1.POC/EXP POC(proof of concept)常指一段漏洞验证代码&#xff1b;EXP(exploit)指利用系统漏洞进行攻击的动作 PoC是证明漏洞存在的,而 Exp 是利用这个漏洞进一步进行攻击&#xff0c;先有POC&#xff0c;才有EXP 2.Payload/shellcode payload&#xff0…

学习周报2.26

文章目录前言文献阅读摘要方法结果深度学习Encoder-Decoder&#xff08;编码-解码&#xff09;信息丢失的问题Attention机制总结前言 This week,I read an article about daily streamflow prediction.This study shows the results of an in-depth comparison between two di…