python装饰器的定义:在代码运行期间在不改变原函数定义的基础上,动态给该函数增加功能的方式称之为装饰器(Decorator)

装饰器的优点和用途:

1. 抽离出大量函数中与函数功能本身无关的的雷同代码并继续重用。
2. 使用装饰器可以将函数“修饰”为完全不同的行为,可以有效的将业务逻辑正交分解,如用于将权限与身份验证从业务中独立出来。
3. 如果一个函数需要一个功能,且这个功能可以被使用在很多函数上,或是函数并不是自己实现,那可以写个装饰器来实现这些功能。
概况来说,装饰器的作用就是为已经存在的对象添加一些额外的功能。

在学习如何运用装饰器前我们先来学习以下几个知识点:

1.变量的作用域:

在python中,函数会创建一个自己的作用域或称之为命名空间,结合以下示例来展示函数命名空间的作用域。

示例代码1:
#coding=utf-
outerVar = "this is a global variable"
def test() :
innerVar = "this is a Local variable"
print ("local variables :")
print (locals()) #locals函数返回的是函数test()内部本地作用域中的可用的变量名称的字典(变量名:值) test()
print ("global variables :")
print (globals()) #globals函数返回的是python程序中的可用的变量名称的字典(变量名:值) #执行结果:
local variables :
{'innerVar': 'this is a Local variable'}
global variables :
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000015848FE87F0>,
'__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'test.py', '__cached__': None,'outerVar': 'this is a global variable',
'test': <function test at 0x0000015848E11E18>}

2.变量解析规则:
在python的作用域规则里面,创建变量时一定会在当前作用域里创建同样的变量,但访问或修改变量时,会在当前作用域中查找该变量,如果没找到匹配的变量,就会依次向上在闭合作用域中进行查找,所以也可以在函数中直接访问全局变量。

示例代码2:
#coding=utf-
outerVar = "this is a global variable"
def test() :
innerVar = "this is a Local variable"
print (outerVar) #获取全局变量outerVar值
print (n) #获取全局变量n的值 n =
test() #执行结果:
this is a global variable

3.变量的生存空间:
   变量不仅仅是存在于一个个的命名空间中,它们还都有自己的生存周期,全局变量的生存周期是在整个程序执行期间有效,而局部变量的生存周期只在当前作用域中有效,一旦这个作用域不存在了,比如函数执行退出了,变量的生存周期就结束了。

示例代码3:
#coding=utf-
outerVar = "this is a global variable"
def test() :
innerVar = "this is a Local variable"
test()
print (innerVar) #test函数执行结束后,innerVar变量已释放,再访问函数内部变量就会报NameError 执行结果:
Traceback (most recent call last):
File "test.py", line , in <module>
print (innerVar)
NameError: name 'innerVar' is not defined

4.嵌套函数:
  定义:嵌套函数就是在函数里面定义函数,而且现有的作用域和变量生存周期依旧不变。

说明:在python里,函数就是对象,它也只是一些普通的值而已。也就是说你可以把函数像参数一样传递给其他的函数或者说从函数了里面返回函数。

示例代码4:
#coding=utf-
def outer() :
name = "python"
def inner() :
#获取name变量值时,python解析器默认会先在函数内部查找,查找失败后,继续往上一层函数作用域查找。
print(name) 
#python解释器会优先在outer的作用域里面查找变量名为inner的变量。把作为函数标识符的变量inner作为返回值返回。
每次函数outer被调用的时候,函数inner都会被重新定义,如果它不被当做变量返回的话,每次执行过后它将不复存在。
return inner()  

print (outer()) 结果:
python
None #inner函数默认返回值为None

嵌套函数返回函数不加()表示返回函数对象,如下示例5所示:

示例代码5:
#coding=utf-
def outer() :
name = "python"
def inner() :
print( name)
return inner #表示返回函数对象 print (outer()) 执行结果:
<function outer.<locals>.inner at 0x0000027446B6A9D8>

5.函数作为变量:
  python中函数可以作为参数来传递

示例代码6:
#coding=utf-
def add(x, y):
return x + y
def sub(x, y):
return x - y
def apply(func, x, y):
return func(x, y) print (apply(add, , )) #add函数作为apply函数的参数传递
print (apply(sub, , )) 执行结果:

6.闭包:

定义:如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包。

一个函数返回的函数对象,这个函数对象执行需要依赖外部函数的变量值,这个时候函数返回的实际内容如下:
    1.函数对象
    2.函数对象执行需要使用的外部变量和变量值。
简而言之:闭包就是返回一个函数和一个函数执行所需要的外部变量。

示例代码7:
#coding=utf-
def outer():
name = "python"
def inner() :
print (name) #函数使用了外部的name变量
return inner #返回函数对象 res = outer() #调用outer()方法,返回inner函数对象
res() #inner函数的对象+()=调用函数inner()
print (res.func_closure)# 查看函数包含哪些外部变量
#print (res()) #注意使用print打印返回结果为name值+inner函数默认返回值None 执行结果:
python
(<cell at 0x0000000002706CD8: str object at 0x0000000002708738>,) #外部变量是一个str类型

上例中的inner()函数就是一个闭包,它本身也是一个函数,而且还可以访问本身之外的变量。

每次函数outer被调用时,inner函数都会被重新定义,示例代码7每次返回的函数inner结果都一样,因为name没变。如下例所示,我们将函数稍微改动
一下,结果就不一样了。

示例代码8:
#coding=utf-
def outer(name) :
def inner() :
print (name)
return inner res1 = outer("python")
res2 = outer("java")
res1()
res2() 执行结果:
python
java

学习了以上6个小知识点,下面开始学习装饰器。

装饰器的定义:
  装饰器其实就是一个闭包,把一个函数当做参数后返回一个替代版函数。

 装饰器分类:

装饰器分为无参数decorator和有参数decorator
无参数decorator:生成一个新的装饰器函数
有参数decorator:装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰。
装饰器的具体定义:
1、把要装饰的方法作为输入参数;
2、在函数体内可以进行任意的操作;
3、只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数,也可以是一个新函数)

装饰器学习七步法:

第一步:最简单的函数,准备附加额外功能

# -*- coding:utf- -*-
#最简单的函数,表示调用了两次函数
def myfunc():
print "myfunc() called."
myfunc()
myfunc() 执行结果:
myfunc() called.
myfunc() called.

第二步:使用装饰函数在函数执行前和执行后分别附加额外功能

# -*- coding:utf- -*-
'''示例2: 替换函数(装饰) ,装饰函数的参数是被装饰的函数对象,返回原函数对象。装饰的实质语句: myfunc = deco(myfunc)'''
def deco(func):
print ("before myfunc() called.") #在func()函数执行前附加功能,打印一句话
func() #被执行函数
print ("after myfunc() called.") #在func()函数执行后附加功能,打印一句话
return func def myfunc():
print ("myfunc()called.") new_myfunc = deco(myfunc) #表示调用参数为myfunc函数对象的deco()函数,结果返回func函数对象并赋值给myfunc
new_myfunc() #表示调用myfunc函数
new_myfunc() #表示调用myfunc函数 结果:
before myfunc() called.
myfunc()called.
after myfunc() called.
myfunc()called.
myfunc()called.
解析:
.myfunc = deco(myfunc)执行结果:
before myfunc() called.
myfunc()called.
after myfunc() called.
.第一次调用myfunc()执行结果:
myfunc()called.
.第二次调用myfunc()执行结果:
myfunc()called.

从第2和第3次调用myfunc函数来看,并没有实现每次调用都返回第1次调用的效果,那么我们要实现每次调用都带有附加功能的效果,我们后面会=逐步实现。

第三步:使用@符号来装饰函数

# -*- coding:utf-8 -*-
'''示例3:使用@符号装饰函数,相当于"myfunc = deco(myfunc)",但发现新函数只在第一次被调用,且原函数多调用了一次。等价于第二步程序 '''
def deco(func):
print ("before myfunc() called.")
func()
print ("after myfunc() called.")
return func @deco
def myfunc():
print ("myfunc()called.") myfunc()
myfunc() #执行结果:
before myfunc() called.
myfunc()called.
after myfunc() called.
myfunc()called.
myfunc()called.

第四步:使用内嵌包装函数来确保每次新函数都被调用

# -*- coding:utf-8 -*-
'''示例4: 使用内嵌包装函数来确保每次新函数都被调用,内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
def _deco():
print ("before myfunc() called.")
func()
print ("after myfunc() called.")
# 不需要返回func,实际上应返回原函数的返回值
return _deco @deco
def myfunc():
print ("myfunc() called.")
return 'ok' myfunc()
myfunc()
执行结果:
before myfunc() called.
myfunc() called.
after myfunc() called.
before myfunc() called.
myfunc() called.
after myfunc() called.

上面是实现了1个函数使用装饰器的例子,下面演示2个函数分别使用装饰器的实例:

# -*- coding:utf-8 -*-
'''增加打印函数执行时间功能,分别统计两个函数的执行效率 '''
import time
def deco(func):
def _deco():
start_time=time.time()
func()
end_time=time.time()
print("执行总耗时:",end_time-start_time)
return _deco @deco
def myfunc():
print (" myfunc() called.")
time.sleep()
return 'true' @deco
def youyfunc():
print (" youyfunc() called.")
time.sleep()
return 'true' myfunc()
print ('*'*)
youyfunc() #结果:
#myfunc() called.
#执行总耗时: 1.0080790519714355
#********************
# youyfunc() called.
#执行总耗时: 2.0119848251342773 #执行过程解析:
执行myfunc()等价于执行deco(myfunc)()
#首先myfunc函数作为参数传递给了deco()函数,形参func被替换为实参myfunc,deco()函数开始顺序执行_deco()函数,
#先调用了myfunc()函数,开始执行myfunc函数,打印执行总耗时,最后返回_deco()函数对象。

说明:使用装饰器的函数之间变量不会互相影响,等于每次调用都会重新生成一个_deco函数。
第五步:实现对带参数的函数进行装饰

# -*- coding:utf-8 -*-
''' 内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象 '''
def deco(func):
def _deco(a, b):
print ("before myfunc() called.")
ret = func(a, b)
print ("after myfunc() called. result: %s" % ret)
return ret
return _deco @deco
def myfunc(a, b):
print ("myfunc(%s,%s) called."%(a, b))
return a + b myfunc(, ) #使用print (myfunc(1, 2)) 查看return ret 的结果
myfunc(, ) 执行结果:
before myfunc() called.
myfunc(,) called.
after myfunc() called. result:
before myfunc() called.
myfunc(,) called.
after myfunc() called. result:

第六步:对参数数量不确定的函数进行装饰

# -*- coding:utf- -*-
'''参数用(*args, **kwargs),自动适应变参和命名参数'''
def deco(func):
def _deco(*args, **kwargs):
print ("before %s called." % func.__name__)
ret = func(*args, **kwargs)
print (" after %s called. result: %s" % (func.__name__, ret))
return ret
return _deco @deco
def myfunc1(a, b):
print (" myfunc(%s,%s) called." % (a, b))
return a+b @deco
def myfunc2(a, b, c):
print (" myfunc2(%s,%s,%s) called." % (a, b, c))
return a+b+c myfunc1(, )
myfunc1(, )
myfunc2(, , )
myfunc2(, , ) #结果
before myfunc1 called.
myfunc(,) called.
after myfunc1 called. result:
before myfunc1 called.
myfunc(,) called.
after myfunc1 called. result:
before myfunc2 called.
myfunc2(,,) called.
after myfunc2 called. result:
before myfunc2 called.
myfunc2(,,) called.
after myfunc2 called. result:

第七步:装饰器带可变参数

# -*- coding:utf- -*-
'''在装饰器第四步4的基础上,让装饰器带参数,和上一示例相比在外层多了一层包装。装饰函数名实际上应更有意义些'''
def deco(arg):
def _deco(func):
def __deco():
print ("before %s called [%s]." % (func.__name__, arg))
func()
print ("after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco @deco("mymodule1") #装饰器参数是一个字符串,本身没有含义
def myfunc():
print (" myfunc() called.") @deco("mymodule2")
def myfunc2():
print (" myfunc2() called.") myfunc() #调用过程等价于:deco("mymodule1")()()-->_deco()()-->__deco()
myfunc2() #解析三组闭包:
. deco("mymodule1")()()+arg-->返回_deco函数对象
. _deco()()+arg+func -->返回__deco函数对象
. __deco()+arg+func --返回函数最终执行结果 执行结果:
before myfunc called [mymodule1].
myfunc() called.
after myfunc called [mymodule1].
before myfunc2 called [mymodule2].
myfunc2() called.
after myfunc2 called [mymodule2].

装饰器顺序:
当同时对一个函数使用多个不同的装饰器进行装饰时,这个时候装饰器的顺序就很重要了。
代码示例:

@A
@B
@C
def f():
pass
等价于:
f = A(B(C(f)))

python之循序渐进学习装饰器的更多相关文章

  1. Python学习---装饰器/迭代器/生成器的学习【all】

    Python学习---装饰器的学习1210 Python学习---生成器的学习1210 Python学习---迭代器学习1210

  2. Python之闭包and装饰器

    闭包和装饰器是Python中非常重要的一种语法格式,在日常工作中应用非常广泛. 首先,我先为大家简单的介绍一下闭包的概念. 闭包:闭包是在函数嵌套的基础上,内层函数使用到外层函数的变量,且外层函数返回 ...

  3. 第7.26节 Python中的@property装饰器定义属性访问方法getter、setter、deleter 详解

    第7.26节 Python中的@property装饰器定义属性访问方法getter.setter.deleter 详解 一.    引言 Python中的装饰器在前面接触过,老猿还没有深入展开介绍装饰 ...

  4. Python中利用函数装饰器实现备忘功能

    Python中利用函数装饰器实现备忘功能 这篇文章主要介绍了Python中利用函数装饰器实现备忘功能,同时还降到了利用装饰器来检查函数的递归.确保参数传递的正确,需要的朋友可以参考下   " ...

  5. python函数与方法装饰器

    之前用python简单写了一下斐波那契数列的递归实现(如下),发现运行速度很慢. def fib_direct(n): assert n > 0, 'invalid n' if n < 3 ...

  6. guxh的python笔记三:装饰器

    1,函数作用域 这种情况可以顺利执行: total = 0 def run(): print(total) 这种情况会报错: total = 0 def run(): print(total) tot ...

  7. python设计模式之内置装饰器使用(四)

    前言 python内部有许多内建装饰器,它们都有特别的功能,下面对其归纳一下. 系列文章 python设计模式之单例模式(一) python设计模式之常用创建模式总结(二) python设计模式之装饰 ...

  8. python 3.x 的装饰器笔记

    今天学到了python的装饰器,感觉这个东西还是稍微有些复杂,所以记录下来,方便以后的查找.虽然标题是python 3.x的装饰器,但是我也没有怎么用过python 2.x,感觉上应该是和python ...

  9. python 中多个装饰器的执行顺序

    python 中多个装饰器的执行顺序: def wrapper1(f1): print('in wrapper1') def inner1(*args,**kwargs): print('in inn ...

随机推荐

  1. cocos2d-x学习之路之工作吐槽

    经过大半年的cocos2d-x的学习,目前已在一个游戏创业公司实习,负责客户端的代码编写和维护.公司做了一款网游.比较给力,马上就要发布了.希望能够大卖.比较坑的是,居然电脑不给联网.查资料都不好查, ...

  2. 洛谷 P1129 解题报告

    P1129 [ZJOI2007]矩阵游戏 题目描述 小\(Q\)是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏.矩阵游戏在一个\(N*N\)黑白方阵进行(如同国际象棋一般 ...

  3. HTML语义化基础

    body { background-color: rgb(60,60,60) } 为使页面呈现较好的内容结构,所以对网页进行布局时,将页面相关元素集中在一起,形成页眉.文章.页脚.侧边栏等元素块,刚开 ...

  4. 杨老师课堂_Java核心技术下之控制台模拟微博用户注册案例

    案例设计背景介绍: 编写一个新浪微博用户注册的程序,要求使用HashSet集合实现.  假设当用户输入用户名.密码.确认密码.生日(输入格式yyyy-mm-dd为正确).手机号码(手机长度为11位,并 ...

  5. Java 使用BigDecimal类处理高精度计算

    Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算.双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的 ...

  6. Tomcat 调优方案

    Tomcat的默认配置,性能并不是最优的,我们可以通过优化tomcat以此来提高网站的并发能力.提高Tomcat的性能可以分为两个方向. 服务器资源 服务器所能提供CPU.内存.硬盘的性能对处理能力有 ...

  7. PAT1035: Password

    1035. Password (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue To prepare f ...

  8. mock测试之powermock

    由于公司框架依赖别的模块, 导致我们开发老是需要跟着他们的脚步, 所以我的上级领导提出这个方案说直接跳过他们,我们自己在本地测试,然后就找了它, 导入相关jar <dependency> ...

  9. Yii 1.1 请求报400错误

    Yii的action可以带参数,比如: class PostController extends CController { public function actionCreate($categor ...

  10. spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)

    最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...