Python第二十六天 python装饰器
Python第二十六天 python装饰器
装饰器
Python 2.4 开始提供了装饰器( decorator ),装饰器作为修改函数的一种便捷方式,为工程师编写程序提供了便利性和灵活性
装饰器本质上就是一个函数,这个函数接受其他函数作为参数,并将其以一个新的修改后的函数进行替换。
装饰器的作用
1、注入参数。为函数提供默认参数,生成新的参数等
2、记录函数的行为。可以统计函数的调用次数,缓存函数的结果,计算函数调用耗费的时间
3、预处理与后处理
4、修改调用时的上下文
函数可以赋值给另外一个变量名
函数可以嵌套
函数对象可以作为另外一个函数的参数
装饰器仅仅是利用前面的这些Python 知识加上Python 语法实现的一种高级语法
函数对象
在Python 语言中, def 语句定义了一个函数对象,并将其赋值给函数名。
也就是说,函数名其实只是一个变量,这个变量引用了这个函数对象。
因此,我们可以将函数赋值给另外一个变量名,通过这个新的变量名调用函数。
def say_hi():
print ("HI")
hello = say_hi
hello()
嵌套函数
在Python 语言中, def 语句是一个实时执行的语句,当它运行的时候会创建一个新的函数对象,并将其赋值给一个变量名
这里所说的变量名就是函数的名称。因为def 是一个语句,因此,函数定义可以出现在其他语句之中。
def outer(x , y) :
def inner () :
return x + y
return inner
f = outer(l , 2)
print(f())
在这个例子中,我们定义了一个名为outer 的函数, 并在outer 函数内部定义了inner 函数
outer 函数以返回值的形式返回inner 函数,我们将返回值保存在变量f 中,f 引用的是outer 函数内部的inner 函数
所以,当我们调用函数f 时,实际是调用的inner 函数
回调函数
回调函数是指将函数作为参数传递给另外一个函数,并在另外一个函数中进行调用。
回调函数并不是Python 语言特有的,在各个编程语言中都存在。
def greeting(f):
f() def say_hi():
print ("HI") def say_hello():
print("HELLO") greeting(say_hi)
greeting(say_hello)
装饰器
在Python 的装饰器语法中,内层函数的参数是被装饰的函数参数,外层函数的参数是被装饰的函数。
def say_hi():
print("hi") def bread(f):
def wrapper(*args, **kwargs ):
print("begin call {0}".format(f.__name__))
f()
print("finish call {0}".format(f.__name__))
return wrapper say_hi_copy = bread(say_hi)
say_hi_copy()
上面这段代码的执行结果如下:
begin call say_hi
hi
finish call say_hi
改造为装饰器
@bread
def say_hi(username='someone'): #被装饰函数,被装饰函数的参数一定要用关键字参数,不能用位置参数,否则不能装入字典!!!!!!!
print("hi")
print(username) def bread(f): #装饰器函数
def wrapper(*args, **kwargs ):
print("begin")
f()
print("finish")
print(kwargs.get("username")) #读取被装饰函数的参数,被装饰函数的参数一定要用关键字参数,不能用位置参数,否则不能装入字典
return wrapper say_hi() 这段程序和前面的程序作用一模一样,产生的结果也相同。区别在于,前面的程序显示地调用了bread 函数来封装say_hi 函数
这段程序通过Python 的语法糖来封装say_hi函数。在Python 中, say_hi 函数定义语句前一行的"@bread"语句表示对该函数应用bread装饰器
其中,"@"是装饰器的语法,"bread"是装饰器的名称
获取正确函数属性
对于一个函数,我们可以通过name 属性得到函数的名字,通过doc 属性得到函数的帮助信息。
但是, 一个被装饰器装饰过的函数,默认情况下,我们通过doc 和name 获取属性时,得到的却是装饰器中嵌套函数的信息
标准库的functools 模块中的wraps 装饰器。wraps 装饰器的作用是,复制函数属性至被装饰的函数
使用functools.wraps 装饰器装饰wrapper 函数。通过这样简单的修改就可以获取到add 函数的正确属性
from __future__ import print_function
import time
import functools def benchmark(func):
@functools.wraps(func)
def wrapper(*args , **kwargs):
t = time.time()
res = func(*args , ** kwargs)
print(func.__name__, time.time() - t)
return res
return wrapper @benchmark
def add (a, b):
'''Calculate the sum of two numbers'''
return a + b print(add.__name__)
print(add.__doc__)
inspect 模块
inspect 模块提供了许多有用的函数来获取活跃对象的信息。其中, getcallargs 函数用来获取函数的参数信息
getcallargs 会返回一个字典,该字典保存了函数的所有参数,包括关键字参数和位置参数。
也就是说, getcallargs 能够根据函数的定义和传递给函数的参数,推测出哪一个值传递给函数的哪一个参数。
getcallargs 推测出这些信息以后,以一个字典的形式返回给我们所有的参数和取值。
import functools
import inspect
def check_is_admin(func):
@functools.wraps(func)
def wrapper(* args, * *kwargs):
func_args = inspect.getcallargs(func , *args , ** kwargs)
if func_args.get('username')! = 'admin' :
raise Exception("This user is not allowed to put/get elem")
return f( * args, * *kwargs )
return wrapper
给装饰器传递参数
在Python 的装饰器语法中,内层函数的参数是被装饰的函数参数,外层函数的参数是被装饰的函数。
那么,如果装饰器本身也有参数应该怎么办呢?在Python 的装饰器语法中, 如果装饰器本身也有参数,则需要再嵌套一层函数。
这也是为什么读者看到的Python装饰器有时候是两层嵌套, 有时候是三层嵌套的原因
下面是一个带参数的装饰器。在这个装饰器的实现中,最外层的函数是装饰器的名称。
这个装饰器的作用是将被装饰的函数执行多次。具体执行的次数由装饰器的参数指定
from __future__ import print_function def times(length=1):
def bread(func):
def wrapper(* args ,* *kwargs):
for i in range(length):
func(*args, **kwargs) # func代表被装饰函数sandwich(),所以被装饰函数有什么参数这里就要写什么,例如Django里面每个view函数都有request参数,装饰view函数就要写成func(request, *args, **kwargs)
return wrapper
return bread @times(5) #5传入到length,执行5次sandwich
def sandwich(name):
print (name) sandwich('hello')
装饰器的先后顺序
例子中,never_cache就要先于login_required被调用。
decorators = [never_cache, login_required] @method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
或
@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
使用method_decorator有时会导致TypeError异常,因为参数传递的原因。当然,一般人碰不到的,碰得到的人都不一般,都能自己查Django官方文档掌握问题原因。
参考
http://www.liujiangblog.com/blog/37/
def record_functime():
'''
函数执行时间统计装饰器
:return:
'''
def stopwatch(callback):
@functools.wraps(callback)
def wrapper(*args, **kwargs):
start = time.time()
body = callback(*args, **kwargs)
end = time.time()
timelog = 'function name:'+callback.__name__ +'; execute time:' +str(start) +'; elapsed time:'+str(end - start)
log_helper.info(timelog)
return body
return wrapper
return stopwatch
Python第二十六天 python装饰器的更多相关文章
- 孤荷凌寒自学python第二十六天python的time模块的相关方法
孤荷凌寒自学python第二十六天python的time模块的相关方法 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 要使用time模块的相关方法,必须在文件顶端引用: import tim ...
- python第二十六课——装饰器
装饰器是闭包的一种使用场景: python中的装饰器在定义上需要传入一个函数对象, 在此函数执行之前或者之后都可以追加其它的操作, 这样做的好处是,在不改变源码(原本业务逻辑的)同时,进行功能的扩展: ...
- 初学 Python(十五)——装饰器
初学 Python(十五)--装饰器 初学 Python,主要整理一些学习到的知识点,这次是生成器. #-*- coding:utf-8 -*- import functools def curren ...
- 孤荷凌寒自学python第二十九天python的datetime.time模块
孤荷凌寒自学python第二十九天python的datetime.time模块 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) datetime.time模块是专门用来表示纯时间部分的类. ...
- 孤荷凌寒自学python第十六天python的迭代对象
孤荷凌寒自学python第十六天python的迭代对象 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 迭代也就是循环. python中的迭代对象有相关的如下几个术语: A容器 contrai ...
- python 装饰器 第十步:装饰器来装饰器一个类
第十步:装饰器来装饰一个类 def kuozhan(cls): print(cls) #声明一个类并且返回 def newHuman(): # 扩展类的功能1 cls.cloth = '漂亮的小裙子' ...
- 简学Python第四章__装饰器、迭代器、列表生成式
Python第四章__装饰器.迭代器 欢迎加入Linux_Python学习群 群号:478616847 目录: 列表生成式 生成器 迭代器 单层装饰器(无参) 多层装饰器(有参) 冒泡算法 代码开发 ...
- Python第二十四天 binascii模块
Python第二十四天 binascii模块 binascii用来进行进制和字符串之间的转换 import binascii s = 'abcde' h = binascii.b2a_hex(s) # ...
- 十一. Python基础(11)—补充: 作用域 & 装饰器
十一. Python基础(11)-补充: 作用域 & 装饰器 1 ● Python的作用域补遗 在C/C++等语言中, if语句等控制结构(control structure)会产生新的作用域 ...
随机推荐
- [ 搭建Redis本地服务器实践系列二 ] :图解CentOS7配置Redis
上一章 [ 搭建Redis本地服务器实践系列一 ] :图解CentOS7安装Redis 详细的介绍了Redis的安装步骤,那么只是安装完成,此时的Redis服务器还无法正常运作,我们需要对其进行一些配 ...
- SSM-MyBatis-01:IDEA的安装,永久注册和简单的MyBatis用例
一,IDEA的安装和永久注册 1.安装: 那到安装包,下一步,选路径,上面可以选操作系统64/32位,下面是程序的默认打开方式,可以不必勾选,也可以全选 路径一定不包含中文,重点 2.永久注册: 将此 ...
- 使用单进程、strace、gdb调试PHP错误
使用单进程.strace.gdb调试PHP错误 PHP一般是在FPM的呵护下运行的,但是某些情况下进程异常崩溃会导致502.下面是解决思想: 1. 单进程运行: php -d display_erro ...
- 监督学习——K邻近算法及数字识别实践
1. KNN 算法 K-近邻(k-Nearest Neighbor,KNN)是分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一.该方法的思路是:如果一个样本在特征空间中的k个最相似( ...
- ajax跨域问题(php)
ajax出现请求跨域错误问题,主要原因就是因为浏览器的"同源策略". 解决方法(我只用过下面这3种): 1. 架设服务器代理:即浏览器请求同源服务器,再由后者请求外部服务(之前博主 ...
- 你不知道的JavaScript--Item9 call(),apply(),bind()与回调
1.call(),apply(),bind()方法 JavaScript 中通过call或者apply用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定 ...
- CentOS6.*安装gitolite
http://www.kankanews.com/ICkengine/archives/64748.shtml 2人收藏此文章, 发表于4小时前(2013-10-22 16:12) , 已有26次阅读 ...
- VMware下对Ubuntu进行扩充磁盘大小
今天用虚拟机的时候,发现虚拟机快满了,提示磁盘空间小,不得不扩充虚拟机空间.经过百度搜索,终于搞定了,记录如下 平台:VMware(10.0.3)+Ubuntu 14.04(32bit) 1.选择VM ...
- MAC下安装如何安装mysql?
最近有位搞开发的朋友,估计是买了一个新的MAC本,他说不是很熟悉这个系统(什么人哪,太懒了!),于是让我给他装一下后台开发过程中经常使用的数据库软件MYSQL,今天给大家分享一下我的操作步骤以及出现问 ...
- stm.go
package) ].ModRevision } func,, ,, ].ModRevision ].Value) }