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

66、Python之函数高级:一个装饰器不够用,可以多装饰器buffer叠加

引言

最近有同学关心一个函数只能被一个装饰器装饰吗?能否同时使用多个装饰器进行装饰?又或者,在定义装饰器的时候,我们应该定义一个“瑞士军刀”式的超强装饰器,还是通过多个装饰器实现增强效果的“buffer”叠加。

今天这篇文章,我们就聊一下关于多装饰器的定义及使用。

本文的内容主要有:

1、单一职能原则

2、多装饰器的使用与注意事项

单一职能原则

在介绍多装饰器的使用之前,我想先简单谈一下面向对象设计原则中的“单一职能原则”。

前面我们曾经提及并简单介绍过面向对象设计的5个基本原则,也就是所谓的“SOLID原则”,这些原则的核心理念在于确保软件设计更加稳定及易于扩展和维护。这些原则分别是:

1、单一职能原则(Single Responsibility Principle, SRP)

2、开闭原则(Open / Closed Principle, OCP)

3、里氏替换原则(Liskov Substitution Principle, LSP)

4、接口隔离原则(Interface Segregation Principle, ISP)

5、依赖倒置原则(Dependency Inversion Principle, DIP)

其中,开闭原则我们已经反复提及到,也结合Python相关语法特性的使用,反复实践了开闭原则。

今天,我们来重点聊一下单一职能原则SRP。

SRP的核心思想是:一个类(模块)应该只有一个引起它变化的原因,也就是说一个类只负责一个职责或者功能。

换句话说,一个类应该只有一个变化的理由。将职责分离到不同的类中,可以使得每个类的目标更加明确、职责更加单一,从而提高代码的可维护性和可扩展性。

单一职责的出发点在于:

1、提高代码的可复用性:类的职责更加单一,本质上也就是更加高内聚、低耦合,使得每个单一职责的类可以像是乐高积木一样,在功能扩展时,可以进行快速的组装、复用。

2、提高代码的可维护性:可维护性其实已经蕴含了可读性,类的职责单一,降低了单个类设计、实现上的复杂度。

3、便于进行单元测试:每个类都天然是一个单一职责的单元,单元测试就变得更加简洁、自然。

为了实现单一职责的落地,可以从以下几个方面着手:

1、职责分离:这是该原则的基本,设计之初就将所有的职责做明确的切分。

2、模块化设计:单一职责的原则,必然要求了模块化的设计理念,单一职责所对应的功能封装到独立的模块或者类中,自然就进行了代码的模块化构建。

3、定期代码重构:很多时候,在设计开发过程中,为了快速上线,或者其他的原因,我们会尽量避免过早优化。所以,不可避免的,可能导致部分功能、模块的设计、开发背离了单一职能原则。为了后续的可维护性和可扩展性,应当定期进行多职责耦合的类的识别及代码重构。

虽然,SRP是面向对象的设计原则,但是在结构化设计或者面向过程的开发中,我们在进行函数的定义及开发中,也是应当遵的。

所以,回到开篇提到的装饰器定义的选择,我们是应该定义一个大而全的瑞士军刀式的装饰器,还是应该定义多个单一职责的装饰?从SRP的角度来看,显然应该进行职责的分离,定义多个小而美的装饰器。

多装饰器的使用与注意事项

基于SRP,我们更加倾向于定义多个小而美的装饰器。但是,一个函数可以使用多个装饰器来装饰、增强吗?

其实,只要回顾一下我们之前提到的关于高阶函数、闭包、装饰器的原理,就知道,显然是可以使用多个装饰器的。多个装饰器作用于同一个函数对象,其实就是对原始函数对象的多层套娃,最终还是函数对象。而且,任意一层套娃得到的函数对象,其函数签名与原始函数签名是一致的。

我们前面分别定义了使用缓存和记录日志的装饰器,我们可以看一下两个装饰器放在一起使用的效果。

为了便于查看,我们对日志装饰器进行简化,直接看代码:

1、日志装饰器函数定义 log.py:

# 简化一下日志记录的定义,便于查看
def log(func):def wrap(*args, **kwargs):print(f'log>> 函数{func.__name__}被调用 参数: {args},{kwargs}')try:res = func(*args, **kwargs)print(f'log>> 函数{func.__name__}被调用 返回值: {res}')return resexcept Exception as e:print(f'log>> 函数{func.__name__}被调用 发生异常: {e}')return wrap

2、缓存装饰器 cache.py:

def cache(func):mem = {}def wrap(*args):res = mem.get(args)if not res:res = mem[args] = func(*args)else:print(f"cache>> 参数[{args}]命中缓存")return resreturn wrap

3、入口文件进行斐波那契函数的计算:

from log import log
from cache import cache@log
@cache
def fibonacci(n):if n == 1:return 0if n == 2:return 1return fibonacci(n - 1) + fibonacci(n - 2)if __name__ == '__main__':# 查看函数对象信息print(fibonacci)fibonacci(5)

执行结果:

146cdcaaac0ac55ad03b297c400968ec.jpeg

如果我们交换一下装饰器的顺序,则执行结果会发生变化:

7b37675bcf2b9144b82a20fc80800f57.jpeg

基于上面的执行过程,我们可以得到以下认知:

多个装饰器同时作用于一个函数时,越靠近函数,越先对函数进行封装

@cache
@log
def fibonacci(n):pass

等价于:fibonacci = cache(log(fibonacci))

最终得到的函数对象,是最外层装饰器封装的结果。

所以,只要理解了@装饰器名的语法糖背后的实现原理,无论装饰器的使用怎么组合、变换,我们都能轻易理解并掌握。

总结

本文以对“大而全”和“小而美”的设计的疑问为出发点,引出了对单一职责原则的理解与实践,并通过遵循SRP的装饰器设计,实现了多个装饰器对原始函数的buffer效果的叠加,看到更加灵活的组合使用。

感谢您的拨冗阅读,希望对您有所帮助。

2a498f6d72ab54da1dfd79e1ecc1712e.jpeg

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 存储虚拟化
  • QT QPrinter无弹窗后台打印
  • 金融壹账通:智能面审解决方案“大显身手”
  • 【未解决】everything软件 中文文件夹 查找不到
  • Java 学习中使用文件、网络连接等资源时,未正确关闭资源,导致资源泄漏应该怎么办?
  • 实现C程序绑定TCP端口
  • 前端封装组件可视化库
  • HTTP 响应状态码详解
  • fileinput pdf编辑初始化预览
  • 【西电电装实习】5. 无人机模块及作用、上位机的操作
  • 【Qt网络编程基础】Tcp服务器和客户端(只支持一对一)
  • Gitea Action注册runner
  • NX—UI界面生成的文件在VS上的设置
  • BT、磁力、种子、直链、PT之间的关系
  • Java后端分布式系统的服务发现:Consul与Eureka的比较
  • angular2 简述
  • CentOS 7 修改主机名
  • gcc介绍及安装
  • magento 货币换算
  • MQ框架的比较
  • Next.js之基础概念(二)
  • ViewService——一种保证客户端与服务端同步的方法
  • vue 配置sass、scss全局变量
  • webgl (原生)基础入门指南【一】
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 说说动画卡顿的解决方案
  • 微信小程序设置上一页数据
  • 学习JavaScript数据结构与算法 — 树
  • 再次简单明了总结flex布局,一看就懂...
  • 智能合约开发环境搭建及Hello World合约
  • 自制字幕遮挡器
  • 白色的风信子
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • ​用户画像从0到100的构建思路
  • !$boo在php中什么意思,php前戏
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #include
  • #宝哥教你#查看jquery绑定的事件函数
  • (5)STL算法之复制
  • (Ruby)Ubuntu12.04安装Rails环境
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (过滤器)Filter和(监听器)listener
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (十)T检验-第一部分
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (原創) 未来三学期想要修的课 (日記)
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • .net core 外观者设计模式 实现,多种支付选择
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接