您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Python

最全面的Python装饰器教程了

时间:2019-10-11 09:17:58  来源:  作者:

装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。在程序开发中经常使用到的功能,合理使用装饰器,能让我们的程序如虎添翼。

私信小编01 获取全套学习教程!

1. 函数名应用

函数名是什么?函数名是函数的名字,本质:变量,特殊的变量。

 函数名就是函数的内存地址,直接打印函数名,就是打印内存地址

def func1():
 print(123)
print(func1)         # <function func1 at 0x0000029042E02E18>

 函数名可以作为变量

def func1():
 print(111)
f = func1
f()           # f() 就是func1() 

 函数名可以作为函数的参数

def func1():
 print(111)
def func2(x):
 x()
func2(func1)         #func1作为func2的参数 

 函数名可以作为函数的返回值

def wrApper():
 def inner():
 print('inner')
 return inner
f = wrapper()
f()

 函数名可以作为容器类类型的元素

使用for循环批量执行函数
def func1():
 print('func1')
def func2():
 print('func2')
def func3():
 print('func3')
l1 = [func1,func2,func3]
for i in l1:
 i()

像上面函数名这种,叫做第一类对象。

第一类对象( first-class object)指:

1.可在运行期创建

2.可用作函数参数或返回值

3.可存入变量的实体

*不明白?那就记住一句话,就当普通变量用

2. 闭包

1、 闭包函数 : 内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数

2、闭包的作用:爬虫、装饰器

当程序执行遇到函数执行时,会在内存空间开辟局部命名空间,当函数执行完毕,该命名空间会被销毁。但是如果这个函数内部形成闭包,则该内存空间不会随着函数执行完而消失。

3、如何判断是否是闭包:print(函数名.__closure__) 结果是 cell 说明是闭包,结果是 None说明不是闭包。

闭包举例

def wrapper():
 name = 'summer'
 def inner():
 print(name)
 inner()
wrapper() # summer

如何判断它是否是一个闭包函数呢? 内层函数名.__closure__ cell 就是=闭包

例 1.

def wrapper():
 name = 'summer'
 def inner():
 print(name)
 inner()
 print(inner.__closure__)
wrapper() 
执行输出:
summer
(<cell at 0x0000017FC9C90B58: str object at 0x0000017FCA349AD0>,)

例 2.

name = 'summer'
def wrapper():
 def inner():
 print(name)
 inner()
 print(inner.__closure__)
wrapper() 
结果输出:
summer
None

返回值为None 表示它不是闭包,因为name是一个全局变量,如果函数调用了外层变量而非全局变量,那么它就是闭包。

例 3.

name = 'summer'
def wrapper2():
 name1 = 'spring'
 def inner():
 print(name)
 print(name1)
 inner()
 print(inner.__closure__)
wrapper2()
结果输出:
summer
spring
(<cell at 0x030B7310: str object at 0x03043680>,)

只要引用了外层变量至少一次,非全局的,它就是闭包

例 4:判断 下面的函数,是一个闭包吗? ******

name = 'summer'
def wraaper2(n):        #相当于n = 'summer' 
  def inner():
 print(n)
 inner()
 print(inner.__closure__) 
wraaper2(name)
结果输出:
summer
(<cell at 0x03867350: str object at 0x037F3680>,)

它也是一个闭包. 虽然wraaper2传了一个全局变量,但是在函数wraaper2内部,inner引用了外层变量,相当于在函数inner外层定义了 n = 'summer',所以inner是一个闭包函数

闭包的好处  当函数开始执行时,如果遇到了闭包,他有一个机制,他会永远开辟一个内存空间,将闭包中的变量等值放入其中,不会随着函数的执行完毕而消失。

举一个例子:爬3次,内存开了3次,很占用内存

from urllib.request import urlopen
content1 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
content2 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
content3 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')

把它封装成闭包

from urllib.request import urlopen
def index():
 url = "https://www.cnblogs.com/"
 def get():
 return urlopen(url).read()
 return get        #return的是get,就是一个函数名
cnblog = index()
print(cnblog) # <function index.<locals>.get at 0x02F46978>
content = cnblog()
print(content) # 页面源码

这个例子,只有第一遍,是从网站抓取的。之后的执行,直接从内存中加载,节省内存空间

3.装饰器

3.1 装饰器初识

装饰器本质: 就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。

import time
def timmer(f):       
 def inner():
 start_time = time.time() 
 f()      
 end_time = time.time() 
 print('此函数的执行时间为{}'.format(end_time - start_time)) 
 return inner   
def func1():   
 print('in func1')       
 time.sleep(1)       
func1 = timmer(func1)       
print(func1)
func1()           # 这里的func1是全新的func1,就是上面的赋值,此时相当于执行 inner函数
输出结果:
<function timmer.<locals>.inner at 0x03822DF8>
in func1
此函数的执行时间为1.0003533363342285

代码从上至下执行

语法糖: 想测试谁,前面加@装饰器函数,即可。 写装饰器,约定俗成,函数名为wrapper

def wrapper(func):
 def inner(*args,**kwargs):
 '''被装饰函数之前'''
 ret = func(*args,**kwargs)
 '''被装饰函数之后'''
 return ret
 return inner
@wrapper
def func(*args,**kwargs):
 print(args,kwargs)
 return 666
print(func())
输出结果:
() {}
666

装饰器利用return制造了一个假象,func()执行,其实是执行inner()  func()把原来的func()给覆盖了

3.2 装饰器传参

例 1: 上面装饰器的例子,func1,要传2个参数a,b

import time
def timmer(f):
 def inner(a,b):
 start_time = time.time()
 f(a,b)
 end_time = time.time()
 print('此函数的执行时间为{}'.format(end_time - start_time))
 return inner
@timmer
def func1(a,b):
 print('in func1 {}{}'.format(a,b))
 time.sleep(1) # 模拟程序逻辑
func1(1,2) 
执行输出:
in func1 12
此函数的执行时间为1.0006024837493896

例 2: 如果有多个参数呢?改成动态参数

import time
def timmer(f):
 def inner(*args,**kwargs):
 start_time = time.time()
 f(*args,**kwargs)
 end_time = time.time()
 print('此函数的执行时间为{}'.format(end_time - start_time))
 return inner
@timmer
def func1(*args,**kwargs):
 print('in func1 {}{}'.format(args,kwargs))
 time.sleep(1) # 模拟程序逻辑
func1(1,2,a='3',b=4) 
执行输出:
in func1 (1, 2){'b': 4, 'a': '3'}
此函数的执行时间为1.000101089477539

函数的执行时,*打散 ;

函数的定义时,*聚合。

from functools import wraps
def wrapper(f): # f = func1
 def inner(*args,**kwargs):       #聚合,args (1,2,3)
 '''执行函数之前的相关操作'''
 ret = f(*args,**kwargs)      # 打散 1,2,3
 '''执行函数之后的相关操作'''
 return ret
 return inner
@wrapper # func1 = wrapper(func1) func1 = inner
def func1(*args):       #args (1,2,3) 聚合
 print(666)
 return args
print(func1(*[1,2,3])) 
执行输出:
666
(1, 2, 3)

例 3 *****

import time #1.加载模块
def timmer(*args,**kwargs): #2.加载变量 5.接收参数True,2,3
 def wrapper(f): #6.加载变量 8.f = func1
 print(args, kwargs) #9.接收timmer函数的值True,2,3
 def inner(*args,**kwargs): #10.加载变量. 13.执行函数inner
 if flag: #14 flag = True
 start_time = time.time() #15 获取当前时间
 ret = f(*args,**kwargs) #16 执行func1
 time.sleep(0.3) #19 等待0.3秒
 end_time = time.time() #20 获取当前时间
 print('此函数的执行效率%f' % (end_time-start_time)) #21 打印差值
 else:
 ret = f(*args, **kwargs)
 return ret #22 返回给函数调用者func1()
 return inner #11 返回给函数调用者wrapper
 return wrapper #7.返回给函数调用timmer(flag,2,3)
flag = True #3 加载变量
@timmer(flag,2,3) # 4.执行函数timmer(flag,2,3) 17.执行函数func1 两步:1,timmer(flag,2,3) 相当于执行wrapper 2.@wrapper 装饰器 func1 = wrapper(func1)
def func1(*args,**kwargs):
 return 666 #18 返回给函数调用者f(*args,**kwargs)
print(func1()) #12 执行函数 

写装饰器,一般嵌套3层就可以了

3.3 多个装饰器,装饰一个函数

def wrapper1(func): # func == f函数名
 def inner1():
 print('wrapper1 ,before func') # 2
 func()
 print('wrapper1 ,after func') # 4
 return inner1
def wrapper2(func): # func == inner1
 def inner2():
 print('wrapper2 ,before func') # 1
 func()
 print('wrapper2 ,after func') # 5
 return inner2
@wrapper2 # f = wrapper2(f) 里面的f==inner1 外面的f == inner2
@wrapper1 # f = wrapper1(f) 里面的f==函数名f 外面的f == inner1
def f(): # 3
 print('in f')
f() # inner2() 
执行输出:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func

哪个离函数近,哪个先计算  最底下的先执行

执行顺序如下图:

这是我见过最全面的Python装饰器教程了

 

多个装饰器,都是按照上图的顺序来的

4. 装饰器的 __name__  __doc___

__name__:函数名

__doc___:函数的解释

普通函数

def func1():
 """
 此函数是完成登陆的功能,参数分别是...作用。
 return: 返回值是登陆成功与否(True,False)
 """
 print(666)
func1()
print(func1.__name__) #获取函数名
print(func1.__doc__) #获取函数名注释说明 
执行输出:
666
func1
此函数是完成登陆的功能,参数分别是...作用。
return: 返回值是登陆成功与否(True,False)

这个有什么用呢?比如日志功能,需要打印出谁在什么时间,调用了什么函数,函数是干啥的,花费了多次时间,这个时候,就需要获取函数的有用信息了

带装饰器的函数

def wrapper(f): # f = func1
 def inner(*args,**kwargs): #聚合, args (1,2,3)
 '''执行函数之前的相关操作'''
 ret = f(*args,**kwargs) # 打散 1,2,3
 '''执行函数之后的相关操作'''
 return ret
 return inner
@wrapper
def func1():
 """
 此函数是完成登陆的功能,参数分别是...作用。
 return: 返回值是登陆成功与否(True,False)
 """
 print(666)
 return True
func1()
print(func1.__name__)
print(func1.__doc__) 
执行输出:
666
inner
执行函数之前的相关操作

函数装饰之后,相当于执行了inner函数,所以输出inner

为了解决这个问题,需要 调用一个模块wraps

wraps将 被修饰的函数(wrapped) 的一些属性值赋值给修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉

from functools import wraps
def wrapper(f): # f = func1
 @wraps(f) #f是被装饰的函数
 def inner(*args,**kwargs): #聚合args (1,2,3)
 '''执行函数之前的相关操作'''
 ret = f(*args,**kwargs) # 打散 1,2,3
 '''执行函数之后的相关操作'''
 return ret
 return inner
@wrapper
def func1():
 """
 此函数是完成登陆的功能,参数分别是...作用。
 return: 返回值是登陆成功与否(True,False)
 """
 print(666)
 return True
func1()
print(func1.__name__)
print(func1.__doc__) 
执行输出:
666
func1
此函数是完成登陆的功能,参数分别是...作用。
return: 返回值是登陆成功与否(True,False)


Tags:Python装饰器   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。在程序开发中经常使用...【详细内容】
2019-10-11  Tags: Python装饰器  点击:(115)  评论:(0)  加入收藏
装饰器给我们带来高效的编程,但也会产生一些问题,而这些问题又容易被我们大家忽视。装饰器产生的副作用在Python世界里,一切皆对象,所以函数也是对象,函数有一些内置属性,例如:__na...【详细内容】
2019-08-29  Tags: Python装饰器  点击:(203)  评论:(0)  加入收藏
介绍首先我要承认,装饰器非常难!你在本教程中看到的一些代码将会有一些复杂。大多数人在学习Python时都跟装饰器做过斗争,所以如果这对你来说很奇怪,不要感到沮丧,因为同样的大...【详细内容】
2019-07-01  Tags: Python装饰器  点击:(296)  评论:(0)  加入收藏
▌简易百科推荐
大家好,我是菜鸟哥,今天跟大家一起聊一下Python4的话题! 从2020年的1月1号开始,Python官方正式的停止了对于Python2的维护。Python也正式的进入了Python3的时代。而随着时间的...【详细内容】
2021-12-28  菜鸟学python    Tags:Python4   点击:(1)  评论:(0)  加入收藏
学习Python的初衷是因为它的实践的便捷性,几乎计算机上能完成的各种操作都能在Python上找到解决途径。平时工作需要在线学习。而在线学习的复杂性经常让人抓狂。费时费力且效...【详细内容】
2021-12-28  风度翩翩的Python    Tags:Python   点击:(1)  评论:(0)  加入收藏
Python 是一个很棒的语言。它是世界上发展最快的编程语言之一。它一次又一次地证明了在开发人员职位中和跨行业的数据科学职位中的实用性。整个 Python 及其库的生态系统使...【详细内容】
2021-12-27  IT资料库    Tags:Python 库   点击:(2)  评论:(0)  加入收藏
菜单驱动程序简介菜单驱动程序是通过显示选项列表从用户那里获取输入并允许用户从选项列表中选择输入的程序。菜单驱动程序的一个简单示例是 ATM(自动取款机)。在交易的情况下...【详细内容】
2021-12-27  子冉爱python    Tags:Python   点击:(4)  评论:(0)  加入收藏
有不少同学学完Python后仍然很难将其灵活运用。我整理15个Python入门的小程序。在实践中应用Python会有事半功倍的效果。01 实现二元二次函数实现数学里的二元二次函数:f(x,...【详细内容】
2021-12-22  程序汪小成    Tags:Python入门   点击:(32)  评论:(0)  加入收藏
Verilog是由一个个module组成的,下面是其中一个module在网表中的样子,我只需要提取module名字、实例化关系。module rst_filter ( ...); 端口声明... wire定义......【详细内容】
2021-12-22  编程啊青    Tags:Verilog   点击:(9)  评论:(0)  加入收藏
运行环境 如何从 MP4 视频中提取帧 将帧变成 GIF 创建 MP4 到 GIF GUI ...【详细内容】
2021-12-22  修道猿    Tags:Python   点击:(6)  评论:(0)  加入收藏
面向对象:Object Oriented Programming,简称OOP,即面向对象程序设计。类(Class)和对象(Object)类是用来描述具有相同属性和方法对象的集合。对象是类的具体实例。比如,学生都有...【详细内容】
2021-12-22  我头秃了    Tags:python   点击:(9)  评论:(0)  加入收藏
所谓内置函数,就是Python提供的, 可以直接拿来直接用的函数,比如大家熟悉的print,range、input等,也有不是很熟,但是很重要的,如enumerate、zip、join等,Python内置的这些函数非常...【详细内容】
2021-12-21  程序员小新ds    Tags:python初   点击:(5)  评论:(0)  加入收藏
Hi,大家好。我们在接口自动化测试项目中,有时候需要一些加密。今天给大伙介绍Python实现各种 加密 ,接口加解密再也不愁。目录一、项目加解密需求分析六、Python加密库PyCrypto...【详细内容】
2021-12-21  Python可乐    Tags:Python   点击:(8)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条