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. Flask入门之上传文件到服务器

    今天要做一个简单的页面,可以实现将文件 上传到服务器(保存在指定文件夹) #Sample.py # coding:utf-8 from flask import Flask,render_templa ...

  2. 连接Access数据遇到的问题总览!

    由于要访问一个厂商的access数据,所以要写一个对于access的demo,相对于mysql.sqlserver来说,连接access花费了不少精力,现在将遇到的问题分享出来,以后大家遇到类似问题时 ...

  3. 设计模式之模板方法(Template Method)

    在整理模板方法之前,先来说点废话吧.除了记录学习总结,也来记录一下生活吧. 我们公司的老板在北京,老板也会因为项目来公司,不过不是天天来.公司有个同事,只要老板不在就天天迟到,而且一天比一天晚,经常来 ...

  4. PAT1058:A+B in Hogwarts

    1058. A+B in Hogwarts (20) 时间限制 50 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue If you ...

  5. j2ee中spring的分布式事务实现及解决方案

    1 java事务类型 Java事务的类型有三种:JDBC事务.JTA(Java Transaction API)事务.容器事务. 常见的容器事务如Spring事务,容器事务主要是J2EE应用服务器提供 ...

  6. vs2017 x64 ibatis.net 平台调用 Oracle.DataAccess, Version=2.112.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342 x64

    遇到的问题: 1.x86无法调用x64 2.调用ibatis.net的providers.config无法通过节点反射查找Oracle.DataAccess, Version=2.112.1.0, C ...

  7. (转)JAVA HashSet 去除重复值原理

    Java中的set是一个不包含重复元素的集合,确切地说,是不包含e1.equals(e2)的元素对.Set中允许添加null.Set不能保证集合里元素的顺序. 在往set中添加元素时,如果指定元素不存 ...

  8. 修改WordPress后台默认登陆地址提高网站安全性

    作者:荒原之梦 原文链接:http://zhaokaifeng.com/?p=536 安装完WordPress后,默认的登陆地址就是: http://XXX.XXX/wordpress/wp-admi ...

  9. Vue.js中前端知识点总结笔记

    1.框架和库的区别: 框架:framework 有着自己的语法特点.都有对应的各个模块库 library 专注于一点 框架的好处: 1.提到代码的质量,开发速度 2.提高代码的复用率 3.降低模块之间 ...

  10. Java 并行与并发

    Java 并行与并发 注意两个词:并行(Concurrent) 并发(Parallel) 并行:是逻辑上同时发生,指在某一个时间内同时运行多个程序 并发:是物理上同时发生,指在某一个时间点同时运行多个 ...