Python 中的装饰器
说到装饰器是我们每个学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 中的装饰器的更多相关文章
- 简单说明Python中的装饰器的用法
简单说明Python中的装饰器的用法 这篇文章主要简单说明了Python中的装饰器的用法,装饰器在Python的进阶学习中非常重要,示例代码基于Python2.x,需要的朋友可以参考下 装饰器对与 ...
- 【Python】python中的装饰器——@
对装饰器本来就一知半解的,今天终于弄清楚了,Python中的装饰器是对装饰者模式的很好运用,简化到骨子里了. python中为什么需要装饰器,看这里:http://www.cnblogs.com/hu ...
- Python 中实现装饰器时使用 @functools.wraps 的理由
Python 中使用装饰器对在运行期对函数进行一些外部功能的扩展.但是在使用过程中,由于装饰器的加入导致解释器认为函数本身发生了改变,在某些情况下——比如测试时——会导致一些问题.Python 通过 ...
- 写python中的装饰器
python中的装饰器主要用于在已有函数实现功能前附加需要输出的信息,下面将用实例展示我如何写装饰器. 首先分别尝试写装饰器装饰一个无参函数和一个有参函数(被装饰函数仅输出,无返回值情况下) def ...
- python中的装饰器decorator
python中的装饰器 装饰器是为了解决以下描述的问题而产生的方法 我们在已有的函数代码的基础上,想要动态的为这个函数增加功能而又不改变原函数的代码 例如有三个函数: def f1(x): retur ...
- python中@property装饰器的使用
目录 python中@property装饰器的使用 1.引出问题 2.初步改善 3.使用@property 4.解析@property 5.总结 python中@property装饰器的使用 1.引出 ...
- 【Python】解析Python中的装饰器
python中的函数也是对象,函数可以被当作变量传递. 装饰器在python中功能非常强大,装饰器允许对原有函数行为进行扩展,而不用硬编码的方式,它提供了一种面向切面的访问方式. 装饰器 一个普通的装 ...
- 三分钟搞定Python中的装饰器
python的装饰器是python的特色高级功能之一,言简意赅得说,其作用是在不改变其原有函数和类的定义的基础上,给他们增添新的功能. 装饰器存在的意义是什么呢?我们知道,在python中函数可以调用 ...
- python cookbook 学习系列(一) python中的装饰器
简介 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓 ...
- python中的装饰器
一.什么是装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能 ...
随机推荐
- 201521123044 《Java程序设计》第8周学习总结
1. 本章学习总结 2. 书面作业 本题作业题集集合 1.List中指定元素的删除(题目4-1) 1.1 实验总结(见注释) public static List<String> conv ...
- 201521123111《Java程序设计》第2周学习总结
1. 本章学习总结 这个星期感觉有点醉,整个人都有点迷茫,仿佛突然失去了方向,连java也难以平复所带来的焦躁的情绪.再加上认为周四早上是没有课的,所以忘记去上了.最近总感觉像咸鱼一样,无所事事,想打 ...
- java:数组操作工具类 java.util.Arrays包 主要方法详解
Arrays类位于Java.util包下,是一个对数组操作的工具类,现将Arrays类中的方法做一个总结(JDK版本:1.6.0_34).Arrays类中的方法可以分为八类: sort(对数组排序) ...
- [06] Java的数据类型
1.基本数据类型 1.1 基本数据类型 byte.chart.short.int.long.float.double.boolean 共8种数据类型为基本数据类型: 数据类型 位数 取 ...
- 02_Ext_Panel
1,面板由以下几个部分组成, 一个顶部工具栏(tbar).一个底部工具栏(bbar).面板头部(header).面板尾部(bottom).面板主区域(body)几个部分组成. 面板类中还内置了面板展开 ...
- mybatis-basedao的实现
package com.yangwei.shop.dao; import java.util.HashMap; import java.util.List; import java.util.Map; ...
- 云计算之路-阿里云上:访问阿里云CDN上的图片,自动跳转到百度首页
昨天有用户向我们反馈一篇博文(一条语句导致CPU持续100%)中的部分图片不能显示,我们的图片访问用的是阿里云CDN,原以为是某个CDN节点不稳定的问题,但在排查时发现这些图片不能显示竟然是因为请求时 ...
- C# 反射结构体struct的一个坑
今天代码用到了反射赋值,代码是这样写的: var objtype = obj.GetType(); var Fieldinfo = objtype.GetField("I64"); ...
- MongoDB中的映射,限制记录和记录拼排序 文档的插入查询更新删除操作
映射 在 MongoDB 中,映射(Projection)指的是只选择文档中的必要数据,而非全部数据.如果文档有 5 个字段,而你只需要显示 3 个,则只需选择 3 个字段即可. find() 方法 ...
- Maven下载、安装和配置(二)
前言 在上篇博文[项目管理和构建]--Maven简介(一)中我们了解到maven是一种全新的项目构建方式,让我们的开发更加简单,高效.Maven主要做的是两件事: 统一开发规范与工具 统一管理jar包 ...