说到装饰器是我们每个学Python人中的心痛。

1.闭包

学习装饰器首先我们先学习闭包:

闭包条件

   1 在一个外函数中定义了一个内函数。

2 内函数里使用了外函数的临时变量。

3 并且外函数的返回值是内函数的引用(即函数名)。

闭包的定义

闭包的概念就是当我们在函数内定义一个函数时,这个内部函数使用了外部函数的临时变量,且外部函数的返回值是内部函数的引用时,我们称之为闭包。

出现的背景

一般情况下,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

闭包的基本结构:

def func1(msg): #外函数
name="xiao"
def inner(): #内函数
na = name # 使用外函数的变量
nb = msg
print(inner.__closure__)
print(inner.__closure__[0].cell_contents)
print(inner.__closure__[1].cell_contents)
return inner #返回内函数的引用
f=func1(123)
f() #判断函数是不是闭包函数用__closure__方法 如果是闭包函数就是返回一个元组包含<cell,
# 如果不是闭包函数就就返回none。这个cell包含了我们被引用的所有外部变量

结果:

(<cell at 0x000001EFD0610CA8: int object at 0x00000000699F7D20>, <cell at 0x000001EFD0610CD8: str object at 0x000001EFD04B4C38>)
123
xiao

闭包存在的意义在哪里?

我认为闭包存在的意义在于它保存了外层函数的变量,如果他不能保存外存函数的变量,那么闭包和其他函数没有什么区别。

例子:

def get_avg():
scores = [] def inner_count_avg(val):
scores.append(val)
return sum(scores)/len(scores) return inner_count_avg avg = get_avg()
print(avg(5)) # 相当于 inner_count_avg(5)
print(avg(6)) # inner_count_avg(6) # 这样看来外函数get_avg只被执行了一次

结果:

5.0
5.5

2.装饰器

装饰器:装饰器是一个函数,这个函数是用来装饰其他函数的,作用:在不改变原函数的调用方式和内部代码为其他函数添加新功能。

装饰器的内层函数就是一个闭包,因为它使用了外部函数的参数func,

原则:1.不能改变被修饰函数的源代码。

2.不能修改被修饰函数的调用方式。

高阶函数

高阶函数:一个函数接收另一个函数名当做参数,或者把另一个函数作为结果返回的函数就是高阶函数.这是<<流畅的Python>>一书中定义的. 装饰器也是高阶函数.

补充:

嵌套函数:在一个函数里定义新的一个函数。

装饰器=高阶函数+嵌套函数

装饰器的两大特性

特性一:能把被装饰的函数替换成其他函数.

特性二:装饰器在加载模块的时候立即执行.

registry = []

def register(func):
print("runnning register(%s)" % func)
registry.append(func)
return func @register
def f1():
print('runing f1()') @register
def f2():
print('runing f2()') def f3():
print('runing f3()') def main():
print('running main()')
print('registery->', registry)
f1()
f2()
f3() if __name__ == '__main__':
main()

在其他py里导入该模块时

runnning register(<function f1 at 0x10ccba840>)
runnning register(<function f2 at 0x10ccba8c8>)

上边的例子说明了加载该模块时,装饰器就已经运行,被装饰器装饰的函数只有在明确的调用的时候才运行.

装饰器例子

例子要求:1.写一个装饰器,计算两个程序运行耗时。

2.不改变源代码和调用方式。

例子:

 # Author :ZHANG
#-*-coding:utf-8-*-
import time
def text1():
time.sleep(3)
print("in the text1")
def text2():
time.sleep(2)
print("in the text2")

方法:

 # Author :ZHANG
#-*-coding:utf-8-*-
import time
def solu(func):
def wreppe():
star_time=time.time()
func()
stop_time=time.time()
print("耗时为%s秒."%(stop_time-star_time))
return wreppe
@solu #这里其实为:text1=solu(text1)
def text1():
time.sleep(3)
print("in the text1")
@solu #同text1
def text2():
time.sleep(2)
print("in the text2")
text1() #这里并不是真的text1(),其实是一个替代,solu(text1)()
text2() #同text1

结果:

 in the text1
耗时为3.000171661376953秒.
in the text2
耗时为2.0001144409179688秒.

这里我们要特别注意这个@符号,其实可以用下面的代码来代替只不过我们用@比较简便罢了,但是我们要知道真正的意义

import time
def solu(func):
def wreppe():
star_time=time.time()
func()
stop_time=time.time()
print("耗时为%s秒."%(stop_time-star_time))
return wreppe
def text1():
time.sleep(3)
print("in the text1")
text1=solu(text1)#这里其实就是变量值替换了
text1() #这里并不是真的text1(),其实是一个替代,solu(text1)()

继续增加要求:text2加上多个参数,包括关键参数

方法:

 # Author :ZHANG
#-*-coding:utf-8-*-
import time
def solu(func):
def wreppe(*args,**kwargs):
star_time=time.time()
func(*args,**kwargs)
stop_time=time.time()
print("耗时为%s秒."%(stop_time-star_time))
return wreppe
@solu #这里其实为:text1=solu(text1)=wreppe
def text1():
time.sleep(3)
print("in the text1")
@solu #同text1
def text2(*args,**kwargs):
time.sleep(2)
print("text2:",args,kwargs)
text1() #这里并不是真的text1(),其实是一个替代,solu(text1)()
text2(2,"we",a=1) #同text1

结果:

 in the text1
耗时为3.000171661376953秒.
text2: (2, 'we') {'a': 1}
耗时为2.0001144409179688秒.

马上到高潮:

新例子要求为:

home函数return后面添加“from home”

 #-*-coding:utf-8-*-
username,password="zhang",12345
def deco(func):
def wreppe(*args,**kwargs):
user=input("enter your name>>>").strip()
passwd=int(input("enter your password").strip())#密码输入这里我简化了许多,现在是为了突出另外一个主题
if user==username and passwd==password:
func(*args,**kwargs)
print("\033[32;1muser has passed authentication\033[0m")
else:
exit("\033[31;1minvalid username or password\033[0m")
return wreppe
def index():
print("welcome to index page")
@deco
def home():
print("welcome to home page")
home()

注意这个home函数添加“”from home”后,如果你打印他会得到如下结果:

enter your name>>>zhang
enter your password12345
welcome to home page
user has passed authentication
None #注意 Process finished with exit code 0

结果return后面得到一个none而不是“from home”,这就不符合装饰器不修改被修饰函数源代码的要求。

方法:

 username,password="zhang",12345
def deco(func):
def wreppe(*args,**kwargs):
user=input("enter your name>>>").strip()
passwd=int(input("enter your password").strip())#密码输入这里我简化了许多,现在是为了突出另外一个主题
if user==username and passwd==password:
res=func(*args,**kwargs) #改变这个区域,把func(*args,**kwargs) 执行结果传给一个变量,就可以了
print("\033[32;1muser has passed authentication\033[0m")
return res #改变这个区域,这里就相当于把res中储存的结果打印出来了,因为最后是print(home())
else:
exit("\033[31;1minvalid username or password\033[0m")
return wreppe
def index():
print("welcome to index page")
@deco
def home():
print("welcome to home page")
return "from home" print(home())

结果:

 enter your name>>>zhang
enter your password12345
welcome to home page
user has passed authentication
from home

还有一种情况:@deco(参数)需要学习

-------------------------------------------------------------------------------------------------第二次题目-----------------------------------------------------------------------

情况一:函数中带有参数

import time
def func(func):
def inner(*args): #因为func2中需要传递参数,所以这里要有*args
tm=args #这里返回的是一个元组
start=time.time()
func(*args) #这里的*args是引用的inner的参数,这里为什么是*args,而不是args,因为*args返回的是一个元组,因为A,B是分开的,所以需要把这个元组打撒
stop=time.time()
print("程序运行时间为{}秒".format(stop-start))
return inner
@func
def func2(a,b):
print("func2:%s"%(a+b)) func2(3,5) #这里的真实意思 func(func2)(3,5)

结果:

func2:8
程序运行时间为0.0秒

情况二: 函数中带有返回值

import time
def func(func):
def inner(*args):
tm=args #这里返回的是一个元组
start=time.time()
set=func(*args)
stop=time.time()
print("程序运行时间为{}秒".format(stop-start))
return set
return inner
@func
def func2(a,b):
print("func2:%s"%(a+b))
return "数学真好" print(func2(3,5))

结果:

func2:8
程序运行时间为0.0秒
数学真好

情况三装饰器中带参数。即有三层函数,通过传入不同的装饰器参数来控制装饰器的行为

给内容添加标签,函数控制内容,装饰器控制添加什么标签


from functools import wraps
def html_tags(tag_name):
"""
给函数添加tag标签
:param tag_name: 标签名字
:return:
"""
def wrapper_func(func):
     @wraps(func)
def wrapper(*args,**kwargs):
content =func(*args,**kwargs)
return "<{tag}>{content}</{tag}>".format(tag=tag_name,content=content)
return wrapper
return wrapper_func
@html_tags("b") #这里就等于 hello = html_tags("b)(hello)
def hello(name='Toby'):
"""
返回要贴标签的内容
:param name:
:return:
"""
return 'hello{}!'.format(name)
b = hello("小花") #这里就等于 html_tags('b')(hello)('小花')
print(b)

结果:

<b>hello小花!</b>

装饰器的固定格式:

from functools import wraps
def wrapper(func):
  @wraps(func)
def inner(*args,**kwargs):
re= func(*args,**kwargs)
return re
return inner

装饰器修复技术(wraps)

装饰器修复技术是用来修复执行函数称为真正的函数,作用将被修饰的函数的某些属性比如__doc__,__name__赋给装饰器中的闭包函数,作用它能保留被装饰函数的名称和docstring。如果你不需要这些属性,wraps就没有什么用。

先不加wraps的情况

def wrapper(func):
def inner(*args,**kwargs):
print("在前面执行")
func()
print('在后面执行')
return inner
@wrapper
def f(): """
这是一个用来测量
:return:
"""
print("hahaa")
if __name__ == '__main__':
print(f.__name__)
print(f.__doc__)

结果为:

我们可以看到执行f()并不是执行真正的f函数,而是执行的wrapper(f)(),也就是真正执行的inner() 函数

inner
None

加wraps的情况

from functools import wraps
def wrapper(func):
@wraps(func)
def inner(*args,**kwargs):
print("在前面执行")
func()
print('在后面执行')
return inner
@wrapper
def f(): """
这是一个用来测量
:return:
"""
print("hahaa")
if __name__ == '__main__':
print(f.__name__) #修饰的函数名
print(f.__doc__) #修饰函数名里边的注释

结果:

f

    这是一个用来测量
:return:

装饰器还具有缓存的功能

当装饰器的外层函数的变量是可变对象的时候,该变量具有计数器的作用,也可以当缓存

def decorator(func):
"""
记录被装饰函数的执行次数
:param func:
:return:
"""
#注意这里使用可变对象
a = [0]
def decorated_func(*args,**keyargs):
func(*args, **keyargs)
#因为闭包是浅拷贝,如果是不可变对象,每次调用完成后符号都会被清空,导致错误
a[0] += 1
print ("%s have bing called %d times" % (func.__name__, a[0]))
return decorated_func @decorator
def func(x):
print (x) func(3)
func(3)

结果:

3
func have bing called 1 times
3
func have bing called 2 times

注意: 如果你把@decorator去掉话,这样调用

decorator(func)(3)
decorator(func)(3)

装饰器就没有了缓存的功能

结果:

3
func have bing called 1 times
3
func have bing called 1 times

具体原因,我也不清楚

装饰器的执行顺序

def one(func):
print('----1----')
def two():
print('----2----')
func()
return two def a(func):
print('----a----')
def b():
print('----b----')
func()
return b @one
@a
def demo():
print('----3----') demo()

结果:

----a----
----1----
----2----
----b----
----3----

我们这里可能大多数人容易猛,我们不用@我们用另外一种方式来写装饰器

def one(func):
print('----1----')
def two():
print('----2----')
func()
return two def a(func):
print('----a----')
def b():
print('----b----')
func()
return b def demo():
print('----3----') one(a(demo))()

结果:

----a----
----1----
----2----
----b----
----3----

我们可以发现这两种方法结果一样,但是第二种装饰器方法容易理解

我们只需要研究它的执行顺序就可以

one(a(demo))()
首先是demo这个参数传进来
第二步是执行a(demo) 结果打印__a__ 返回b函数名
第三步是执行one(b)函数,把b当做函数穿进去,打印__1__,返回two
第四步是执行two(),打印__2__,执行b函数,打印__b__,执行demo函数

总结执行顺序:

先调用@a执行a函数的外函数,--->one函数的外函数---->one内函数---->a函数的内函数---->执行demo函数

按照实现功能来说:

先实现one函数的功能,再实现a函数的功能

在实际应用的场景中,当我们采用上面的方式写了两个装饰方法比如先验证有没有登录 @login_required , 再验证权限够不够时 @permision_allowed 时,我们采用下面的顺序来装饰函数:

@login_required
@permision_allowed
def f()
# Do something
return

Python 中的装饰器的更多相关文章

  1. 简单说明Python中的装饰器的用法

    简单说明Python中的装饰器的用法 这篇文章主要简单说明了Python中的装饰器的用法,装饰器在Python的进阶学习中非常重要,示例代码基于Python2.x,需要的朋友可以参考下   装饰器对与 ...

  2. 【Python】python中的装饰器——@

    对装饰器本来就一知半解的,今天终于弄清楚了,Python中的装饰器是对装饰者模式的很好运用,简化到骨子里了. python中为什么需要装饰器,看这里:http://www.cnblogs.com/hu ...

  3. Python 中实现装饰器时使用 @functools.wraps 的理由

    Python 中使用装饰器对在运行期对函数进行一些外部功能的扩展.但是在使用过程中,由于装饰器的加入导致解释器认为函数本身发生了改变,在某些情况下——比如测试时——会导致一些问题.Python 通过  ...

  4. 写python中的装饰器

    python中的装饰器主要用于在已有函数实现功能前附加需要输出的信息,下面将用实例展示我如何写装饰器. 首先分别尝试写装饰器装饰一个无参函数和一个有参函数(被装饰函数仅输出,无返回值情况下) def ...

  5. python中的装饰器decorator

    python中的装饰器 装饰器是为了解决以下描述的问题而产生的方法 我们在已有的函数代码的基础上,想要动态的为这个函数增加功能而又不改变原函数的代码 例如有三个函数: def f1(x): retur ...

  6. python中@property装饰器的使用

    目录 python中@property装饰器的使用 1.引出问题 2.初步改善 3.使用@property 4.解析@property 5.总结 python中@property装饰器的使用 1.引出 ...

  7. 【Python】解析Python中的装饰器

    python中的函数也是对象,函数可以被当作变量传递. 装饰器在python中功能非常强大,装饰器允许对原有函数行为进行扩展,而不用硬编码的方式,它提供了一种面向切面的访问方式. 装饰器 一个普通的装 ...

  8. 三分钟搞定Python中的装饰器

    python的装饰器是python的特色高级功能之一,言简意赅得说,其作用是在不改变其原有函数和类的定义的基础上,给他们增添新的功能. 装饰器存在的意义是什么呢?我们知道,在python中函数可以调用 ...

  9. python cookbook 学习系列(一) python中的装饰器

    简介 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓 ...

  10. python中的装饰器

    一.什么是装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能 ...

随机推荐

  1. 团队作业4——第一次项目冲刺(ALpha版本)第四天

    一.Daily Scrum Meeting照片 二.燃尽图 三.项目进展 1.界面 完善了昨天的的代码---前端的HTML页面设计 2.功能 完成后台数据处理的全部基本功能: a.数据结构设计及数据交 ...

  2. bean的生命周期以及延迟实例化

    可以指定bean的初始化创建的时候调用的方法,以及销毁的时候调用的方法. 通过指定中的init-method和destroy-method方法指定bean的创建和销毁的时候执行类中的方法. 把lazy ...

  3. 201521123038 《Java程序设计》 第六周学习总结

    201521123038 <Java程序设计> 第六周学习总结 1. 本周学习总结 2. 书面作业 1.clone方法 1.1 Object对象中的 clone 方法是被protected ...

  4. 201521123105《jave程序》第二周学习总结

    1. 本周学习总结 学习了各种java数据类型以及各种运算符的使用 学习了一维,二维数组的用法 学习了String类对象使用 2. 书面作业 使用Eclipse关联jdk源代码,并查看String对象 ...

  5. 201521123114 《Java程序设计》第12周学习总结

    1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象(属性:int id, String name,int age,double ...

  6. chrome保存网页为单个文件(mht格式)

    网页归档(英语:MIME HTML或MIME Encapsulation of Aggregate HTML Documents,又称单一文件网页或网页封存盘案)为以多用途互联网邮件扩展格式,将一个多 ...

  7. PowerBI开发 第四篇:DAX表达式

    DAX 表达式主要用于创建度量列(Measure),度量值是根据用户选择的Filter和公式,计算聚合值,DAX表达式基本上都是引用对应的函数,函数的执行有表级(Table-Level)上下文和行级( ...

  8. Windbg调试(关于句柄表的获取,32位)

    今天利用Windbg(x86)进行了获得句柄表的调试,从中获益良多,对调试步骤和按键又一次进行了熟悉,对于句柄表页的概念更是得到了进一步的清晰认识.windbg调试和句柄表不熟悉的朋友可以借鉴我的调试 ...

  9. Spring02-AOP

    1,动态代理,指的是通过一个代理对象创建需要的业务对象,然后在这个代理对象中统一进行各种操作. 步骤: 1)写一个类实现InvocationHandler接口: 2)创建要代理的对象 2,创建一个简单 ...

  10. Python的json and pickle序列化

    json序列化和json反序列化 #!/usr/bin/env python3 # -*- coding: utf-8 -*- __author__ = '人生入戏' import json a = ...