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)会产生新的作用域 ...
随机推荐
- springMVC(spring)+WebSocket案例(获取请求参数)
开发环境(最低版本):spring 4.0+java7+tomcat7.0.47+sockjs 前端页面要引入: <script src="http://cdn.jsdelivr.ne ...
- PAT1081:Rational Sum
1081. Rational Sum (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given N ...
- Elasticsearch结构化搜索_在案例中实战使用term filter来搜索数据
1.根据用户ID.是否隐藏.帖子ID.发帖日期来搜索帖子 (1)插入一些测试帖子数据 POST /forum/article/_bulk { "index": { "_i ...
- Re:从零开始的领域驱动设计
领域驱动的火爆程度不用我赘述,但是即便其如此得耳熟能详,但大多数人对其的认识,还只是停留在知道它的缩写是DDD,知道它是一种软件思想,或者知道它和微服务有千丝万缕的关系.Eric Evans对DDD的 ...
- 这样入门asp.net core 之 静态文件
本文章主要说明asp.net core中静态资源处理方案: 一.静态文件服务 首先明确contentRoot和webroot这两个概念 contentRoot:web的项目文件夹,其中包含webroo ...
- 用react重构个人网站 3-23
1: :before 选择器在被选元素的内容前面插入内容 2: float是什么?浮动在CSS中的作用 3:CSS运用在React中的两种方式 使用className属性,CSS内容就用link加 ...
- Linux 6.8 TFS(Taobao File System)使用文档-安装篇
介绍 TFS(Taobao FileSystem)是一个高可扩展.高可用.高性能.面向互联网服务的分布式文件系统,其设计目标是支持海量的非结构化数据的存储:TFS使用C++语言开发,需要运行在64b ...
- 把封装脚本做成jar包
前提: eclipse, selenium, maven 把二次封装过的脚本做成jar包, 这样可以在新建工程里也调用封装过的方法. 实现步骤: 1. project 右键 => maven = ...
- Golang中使用lua进行扩展
前言 最近在项目中需要使用lua进行扩展,发现github上有一个用golang编写的lua虚拟机,名字叫做gopher-lua.使用后发现还不错,借此分享给大家. 数据类型 lua中的数据类型与go ...
- I春秋——Misc(贝斯家族)
不好意思呀!最近一直忙着学习python,竟然忘记更新I春秋里面的题目了(QAQ),我以后会时时刻刻提醒自己要更新 永远爱你们的! ----新宝宝 1:贝斯家族: 解题思路:我相信看见这一题都能够想到 ...