深入了解python装饰器

深入了解python装饰器

目录

一、装饰器

1.相关知识点

2.语法糖

3.装饰器模板

4.有参装饰器

一、装饰器 1.相关知识点

*args:负责将多余的位置实参汇总,赋值给args

**kwargs:负责将多余的关键字实参汇总,赋值给kwargs

命名空间与作用域

函数对象:

可以把函数当成参数传入

可以把函数当做返回值返回

函数的嵌套定义:在函数内定义函数

闭包函数:父函数的返回值为一个函数,被返回的函数调用了父函数的局部变量,且该函数可以在父函数外部执行

装饰器:

装饰器:定义一个为其他函数添加功能的函数

为什么要使用装饰器?

开放封闭原则:开放扩展功能但封闭源代码的修改

装饰器就是在不修改装饰对象源代码以及调用方式的前提下,为装饰对象添加新功能

装饰器实现:

需求:不修改源代码,计算代码执行时间

 源代码:

import time def func0(x):     time.sleep(1)     print(x) func0(0) # 方案一:实现了计算代码执行时间的功能,但修改了源代码,不符合要求 def func1(x):     start = time.time()     time.sleep(1)     print(x)     end = time.time()     print('方案一 运行时间:{}'.format(end - start)) func1(1) # 方案二:满足要求,但代码不具备重用性 start = time.time() func0(2) end = time.time() print('方案二 运行时间:{}'.format(end - start)) # 方案三:将方案二封装为函数,但修改了函数的调用方式,不符合要求 def wrapper():     start = time.time()     func0(3)     end = time.time()     print('方案三 运行时间:{}'.format(end - start)) wrapper() # 方案四:不修改原函数的调用方式 def wrapper(x):     start = time.time()     func0(x)     end = time.time()     print('方案四 运行时间:{}'.format(end - start)) wrapper(4) # 方案五:方案四参数被写死,优化方案四 def wrapper(*args, **kwargs):     start = time.time()     func0(*args, **kwargs)     end = time.time()     print('方案五 运行时间:{}'.format(end - start)) wrapper(5) # 方案六:方案五函数被写死,优化方案五,但修改了源代码的调用方式 def outter(a):     def wrapper(*args, **kwargs):         start = time.time()         a(*args, **kwargs)         end = time.time()         print('方案六 运行时间:{}'.format(end - start))     return wrapper f = outter(func0) f(6) # 方案七:优化方案六(outter即为装饰器,用于装饰func0) def outter(a):     def wrapper(*args, **kwargs):         start = time.time()         a(*args, **kwargs)         end = time.time()         print('方案七 运行时间:{}'.format(end - start))     return wrapper func0 = outter(func0)    # 偷梁换柱,调用者无感知 func0(7)

运行结果:

0
1
方案一 运行时间:1.001857042312622
2
方案二 运行时间:1.0040733814239502
3
方案三 运行时间:1.0017154216766357
4
方案四 运行时间:1.007995367050171
5
方案五 运行时间:1.0145602226257324
6
方案六 运行时间:1.0046615600585938
7
方案七 运行时间:1.0094060897827148

2.语法糖

使用语法糖,需要将装饰器放到被装饰对象前

# 不使用语法糖 import time def func0(x):     time.sleep(1)     print(x) # func0(0) def outter(a):     def wrapper(*args, **kwargs):         start = time.time()         a(*args, **kwargs)         end = time.time()         print('运行时间:{}'.format(end - start))     return wrapper func0 = outter(func0) func0('hello') # 使用语法糖 import time def outter(a):     def wrapper(*args, **kwargs):         start = time.time()         a(*args, **kwargs)         end = time.time()         print('运行时间:{}'.format(end - start))     return wrapper @outter # 语法糖,等价于该行func0 = outter(func0) def func0(x):     time.sleep(1)     print(x) func0('hello') print(func0.__name__) 

运行结果:

hello
运行时间:1.0050427913665771
wrapper

3.装饰器模板  # 装饰器模板  def decorator_name(x):      def wrapper(*args, **kwargs):          # ...新添加的功能...          # 下为调用原函数的格式          x(*args, **kwargs)      return wrapper @decorator_name  def func_name():      pass

扩展:真正实现偷梁换柱,调用者无感知

不使用装饰器

 # 不使用装饰器 import time def func0(x):     time.sleep(1)     print(x) func0('hello') print(func0.__name__) # 查看函数名  print(help(func0)) # 查看帮助信息(主要为注释内容)

运行结果:

hello
func0
Help on function func0 in module main:

func0(x)
这是函数

None

使用装饰器:

import time def outter(a):     def wrapper(*args, **kwargs):         '''这是装饰器'''         start = time.time()         a(*args, **kwargs)         end = time.time()         print('运行时间:{}'.format(end - start))     return wrapper @outter # func0 = outter(func0) def func0(x):     '''这是函数'''     time.sleep(1)     print(x) func0('hello') print(func0.__name__) print(help(func0))

运行结果:

hello
运行时间:1.011878490447998
wrapper
Help on function wrapper in module main:

wrapper(*args, **kwargs)
这是装饰器

None

呕吼,露馅了

解决方法,将原函数的属性和方法,赋值给装饰器内的wrapper

import time def outter(a):     def wrapper(*args, **kwargs):         '''这是装饰器'''         start = time.time()         a(*args, **kwargs)         end = time.time()         print('运行时间:{}'.format(end - start))     wrapper.__name__ = a.__name__     wrapper.__doc__ = a.__doc__     return wrapper @outter # func0 = outter(func0) def func0(x):     '''这是函数'''     time.sleep(1)     print(x) func0('hello') print(func0.__name__) print(help(func0))

运行结果:

hello
运行时间:1.0030155181884766
func0
Help on function func0 in module main:

func0(*args, **kwargs)
这是函数

None

但是,函数有很多属性和方法,一个一个手动修改过于麻烦,甚至可能会遗漏,但python也提供了解决方法

import time from functools import wraps def outter(a):     @wraps(a)     def wrapper(*args, **kwargs):         '''这是装饰器'''         start = time.time()         a(*args, **kwargs)         end = time.time()         print('运行时间:{}'.format(end - start))     # wrapper.__name__ = a.__name__     # wrapper.__doc__ = a.__doc__     return wrapper @outter # func0 = outter(func0) def func0(x):     '''这是函数'''     time.sleep(1)     print(x) func0('hello') print(func0.__name__) print(help(func0))

运行结果:

hello
运行时间:1.0114128589630127
func0
Help on function func0 in module main:

func0(x)
这是函数

None

4.有参装饰器

装饰器内需要传入参数,但是由于语法糖的限制,装饰器只能有一个参数,并且该参数仅用来接收被装饰对象的内存地址,如何传入其他参数?

解决思路:将装饰器做成闭包函数

def outter(db_type):     # 装饰器auth     def auth(x):         def wrapper(*args, **kwargs):             if db_type == 'file':                 name = input('请输入姓名:')                 passwd = input('请输入密码:')                 if name == 'zhangsan' and passwd == '123':                     x(*args, **kwargs)                     print('基于文件认证')                 else:                     print('用户名或密码错误,认证失败')             elif db_type == 'mysql':                 x(*args, **kwargs)                 print('基于数据库认证')             else:                 print('未知认证方式,不支持')         return wrapper     return auth # 函数 auth = outter(db_type='file') @auth def file():     print('hello') auth = outter(db_type='mysql') @auth def mysql():     print('world') msg = input('请选择认证方式(1=file|2=mysql):').strip() if msg =='1':     file() elif msg == '2':     mysql() else:     print('不支持')

将传入的参数写入语法糖

def outter(db_type):     # 装饰器auth     def auth(x):         def wrapper(*args, **kwargs):             if db_type == 'file':                 name = input('请输入姓名:')                 passwd = input('请输入密码:')                 if name == 'zhangsan' and passwd == '123':                     x(*args, **kwargs)                     print('基于文件认证')                 else:                     print('用户名或密码错误,认证失败')             elif db_type == 'mysql':                 x(*args, **kwargs)                 print('基于数据库认证')             else:                 print('未知认证方式,不支持')         return wrapper     return auth # 函数 # auth = outter(db_type='file') # @auth @outter(db_type='file') def file():     print('hello') # auth = outter(db_type='mysql') # @auth @outter(db_type='mysql') def mysql():     print('world') msg = input('请选择认证方式(1=file|2=mysql):').strip() if msg =='1':     file() elif msg == '2':     mysql() else:     print('不支持')

模板

# 有参装饰器模板 def out_decorator_name(x,y,z):     def decorator_name(a):         def wrapper(*args, **kwargs):             # ...新添加的功能...             # 下为调用原函数的格式             a(*args, **kwargs)         return wrapper     return decorator_name @out_decorator_name(x,y,z=1) def func_name():      pass

到此这篇关于深入了解python装饰器的文章就介绍到这了,更多相关python装饰器内容请搜索易知道(ezd.cc)以前的文章或继续浏览下面的相关文章希望大家以后多多支持易知道(ezd.cc)!

推荐阅读

    excel怎么用乘法函数

    excel怎么用乘法函数,乘法,函数,哪个,excel乘法函数怎么用?1、首先用鼠标选中要计算的单元格。2、然后选中单元格后点击左上方工具栏的fx公

    opporeno8参数配置及价格

    opporeno8参数配置及价格,面部,亿元,Oppo的荣誉2020年1月4日,接近屏幕关闭传感器是否支持双卡:支持oppor11splus什么时候上市的Oppo R11S P

    excel中乘法函数是什么?

    excel中乘法函数是什么?,乘法,函数,什么,打开表格,在C1单元格中输入“=A1*B1”乘法公式。以此类推到多个单元。1、A1*B1=C1的Excel乘法公式

    魅蓝note6性能参数有哪些

    魅蓝note6性能参数有哪些,摄像头,蓝牙,魅蓝note6性能参数有哪些魅力蓝色Note6最好拍照。电池寿命更长。蓝色Note6使用高通 snapdragon 625

    标准差excel用什么函数?

    标准差excel用什么函数?,函数,标准,什么,在数据单元格的下方输入l标准差公式函数公式“=STDEVPA(C2:C6)”。按下回车,求出标准公差值。详细

    设置总账参数|用友u8设置总账参数

    设置总账参数|用友u8设置总账参数,,1. 用友u8设置总账参数1、首先要点开数据权限控制设置;2、选择想要设置控制的单据;3、打开后看到左上角

    csgo参数设置|csgo怎么保存

    csgo参数设置|csgo怎么保存,,csgo怎么保存第一步下载csgo的官方版本。然后再下载一个5e对战平台,PS:5e的账号和csgo的账号不是一个账号。第

    移动apn设置|移动apn设置参数

    移动apn设置|移动apn设置参数,,移动apn设置参数1、打开手机系统设置界面应用,点击页面中的“移动网络”设置选项。2、进入移动网络设置页面