当前位置:
首页 > Python基础教程 >
-
【2020Python修炼记19】Python语法入门—函数装饰器
一、装饰器的介绍
1、为何要用装饰器
开放封闭原则——
软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,
而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。
2、什么是装饰器
’装饰’代指为被装饰对象添加新的功能,
’器’代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。
即 装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。
装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,
有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
提示:可调用对象有函数,方法或者类,此处我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数。
函数装饰器,指的是定义一个函数,该函数是用来为其他函数添加额外的功能。
二、函数装饰器的实现
函数装饰器分为:无参装饰器和有参装饰器两种,二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。
遵循原则:不修改被装饰对象源代码&调用方式
1、无参装饰器的实现
(请紧跟步伐——)
(0)需求——在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能
def index(x,y): time.sleep(3) print('index %s %s' %(x,y)) index(111,222) # index(y=111,x=222) #其他调用的参数形式 # index(111,y=222)
(1)解决方案一:失败
问题:没有修改被装饰对象的调用方式,但是修改了其源代码
import time def index(x,y): start=time.time() time.sleep(3) print('index %s %s' %(x,y)) stop = time.time() print(stop - start) index(111,222)
(2)解决方案二:失败
问题:没有修改被装饰对象的调用方式,也没有修改了其源代码,并且加上了新功能,但是代码冗余
import time def index(x,y): time.sleep(3) print('index %s %s' %(x,y)) start=time.time() index(111,222) stop=time.time() print(stop - start) start=time.time() index(111,222) stop=time.time() print(stop - start) start=time.time() index(111,222) stop=time.time() print(stop - start)
(3)解决方案三:失败
问题:解决了方案二代码冗余问题,但带来一个新问题即函数的调用方式改变了
import time def index(x,y): time.sleep(3) print('index %s %s' %(x,y)) def wrapper(): start=time.time() index(111,222) stop=time.time() print(stop - start) wrapper()
(4)优化方案三的大方向:如何在方案三的基础上,不改变函数的调用方式
方案三的优化一:将index的参数写活了
import time def index(x,y,z): time.sleep(3) print('index %s %s %s' %(x,y,z)) def wrapper(*args,**kwargs): start=time.time() index(*args,**kwargs) # index(3333,z=5555,y=44444) stop=time.time() print(stop - start) # wrapper(3333,4444,5555) # wrapper(3333,z=5555,y=44444)
方案三的优化二:在优化一的基础上把被装饰对象写活了,原来只能装饰index
import time def index(x,y,z): time.sleep(3) print('index %s %s %s' %(x,y,z)) def home(name): time.sleep(2) print('welcome %s to home page' %name) def outter(func): # func = index的内存地址 def wrapper(*args,**kwargs): start=time.time() func(*args,**kwargs) # index的内存地址() stop=time.time() print(stop - start) return wrapper index=outter(index) # index=wrapper的内存地址 home=outter(home) # home=wrapper的内存地址 home('egon') # home(name='egon')
方案三的优化三:将wrapper做的跟被装饰对象一模一样,以假乱真
import time def index(x,y,z): time.sleep(3) print('index %s %s %s' %(x,y,z)) def home(name): time.sleep(2) print('welcome %s to home page' %name) def outter(func): def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print(stop - start) return res return wrapper # 偷梁换柱:home这个名字指向的wrapper函数的内存地址 home=outter(home) res=home('egon') # res=wrapper('egon') print('返回值--》',res)
(5)语法糖——让你开心的语法糖,可以含在嘴里很久很久
如何定义并使用装饰器——
== 定义装饰器
== 使用装饰器:在被装饰对象正上方的单独一行写@装饰器名字
import time #定义装饰器 def timmer(func): def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print(stop - start) return res return wrapper # 使用装饰器:在被装饰对象正上方的单独一行写@装饰器名字 @timmer # index=timmer(index) def index(x,y,z): time.sleep(3) print('index %s %s %s' %(x,y,z)) @timmer # home=timmer(ome) def home(name): time.sleep(2) print('welcome %s to home page' %name) index(x=1,y=2,z=3) home('egon')
(6)总结无参装饰器模板
模板:
def outter(func): def wrapper(*args,**kwargs): # 1、调用原函数 # 2、为其增加新功能 res=func(*args,**kwargs) return res return wrapper
糖栗子:
def auth(func): def wrapper(*args, **kwargs): # 1、调用原函数 # 2、为其增加新功能 name = input('your name>>: ').strip() pwd = input('your password>>: ').strip() if name == 'egon' and pwd == '123': res = func(*args, **kwargs) return res else: print('账号密码错误') return wrapper @auth def index(): print('from index') index()
(7)吃完糖,思考一下——叠加多个装饰器时,加载顺序与运行顺序是怎样的?
@deco1 # index=deco1(deco2.wrapper的内存地址) @deco2 # deco2.wrapper的内存地址=deco2(deco3.wrapper的内存地址) @deco3 # deco3.wrapper的内存地址=deco3(index) def index(): pass
2、有参装饰器的实现
参考资料:
https://zhuanlan.zhihu.com/p/109078881
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式